Your browser lacks required capabilities. Please upgrade it or switch to another to continue.
Loading…
{
"name": "Vesper",
"ifid": "7032a282-b49c-5de1-9ef0-8553b88d1f63",
"format": "SugarCube",
"format-version": "2.36.1",
"start": "Start"
}<<set $player = {
"name": "Wren",
"portrait": "wren.jpg",
"current_location": "",
"core_traits": {"energy": 100, "money": 50, "fighting": 0, "stealth": 0, "yard_depth": 0, "weapon_repair": 0, "coin": 0, "equipped_weapon": 1, "drain_charge": 1, "arousal_charge": 0, "corruption": 0, "arousal": 0, "hygiene": 100, "sex_stage": 0, "loop_npc_pleasure": 0, "sex_finisher_type": 0, "anal_active": 0, "sex_entry_origin": 0, "drains_done": 0, "names_known": 0, "core_strain": 0, "cell_door": 0, "core_sealed": 0, "kess_debt": 0, "repair_armed": 0, "repair_session": 0, "calloway_drains_done": 0, "colm_drains_done": 0, "case_progress": 0, "vault_cleared": 0, "emitter_broken": 0, "feed_line_days": 0, "part_held": 0, "part_installed": 0, "mercer_attempts": 0, "part_spec": 0, "spec_pending": 0, "case_held": 0, "mercer_drains_done": 0, "mercer_leash_state": 0, "controller_seated": 0, "controller_through": 0, "controller_state": 0, "bastien_drains_done": 0, "wall_refusals": 0, "bar_rung": 0, "taser_held": 0, "taser_through": 0, "ruin_depth": 0},
"wardrobe": {"company_grays": {"id": "company_grays", "name": "Company grays", "slot": "dress", "image": "./videos/clothing/company_grays.jpg"}, "plain_bra": {"id": "plain_bra", "name": "Plain bra", "slot": "bra", "image": "./videos/clothing/plain_bra.jpg"}, "plain_briefs": {"id": "plain_briefs", "name": "Plain briefs", "slot": "underwear", "image": "./videos/clothing/plain_briefs.jpg"}, "flat_shoes": {"id": "flat_shoes", "name": "Flats", "slot": "shoes", "image": "./videos/clothing/flat_shoes.jpg"}},
"equipped": {"bra": "plain_bra", "underwear": "plain_briefs", "top": null, "bottom": null, "dress": "company_grays", "legwear": null, "shoes": "flat_shoes"}
}>><<set $npcs = {"npc_mercer": {"name": "Mercer", "portrait": "mercer.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_renner": {"name": "Renner", "portrait": "renner.jpg", "core_traits": {"relation": 0, "corruption": 0, "arousal": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_sol": {"name": "Sol", "portrait": "sol.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_bastien": {"name": "Bastien", "portrait": "bastien.jpg", "core_traits": {"relation": 0, "corruption": 0, "arousal": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_rue": {"name": "Rue", "portrait": "rue.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_marsh": {"name": "Marsh", "portrait": "marsh.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_kess": {"name": "Kess", "portrait": "kess.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_calloway": {"name": "Calloway", "portrait": "calloway.jpg", "core_traits": {"relation": 0, "corruption": 0, "arousal": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_vane": {"name": "Vane", "portrait": "vane.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_colm": {"name": "Colm", "portrait": "colm.jpg", "core_traits": {"relation": 0, "corruption": 0, "arousal": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_enns": {"name": "Enns", "portrait": "enns.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_voss": {"name": "Voss", "portrait": "voss.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_reyes": {"name": "Reyes", "portrait": "reyes.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_marr": {"name": "Marr", "portrait": "marr.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}, "npc_cain": {"name": "Cain", "portrait": "cain.jpg", "core_traits": {"relation": 0}, "flags": {}, "schedule": [], "relationship": ""}}>><<set $npc_interacted_today = {}>><<set $flags = {"game_started": true, "debug_mode": false}>><<set $flags_meta = {}>><<set $game_state = {
"current_canvas": "",
"visited_locations": [],
"visited_nodes": [],
"trigger_history": {},
"activity_trigger_history": {},
"visited_choices": {},
"active_modifiers": {},
"random_cooldowns": {},
"stage_advancement_log": {},
"quests": {},
"scheduled": [],
"fast_jobs": { "xp": 0, "cooldowns": {} },
"bank": { "balance": 0 },
"time_state": {
"current_hour": 20,
"current_minute": 0,
"current_day": "Monday",
"current_week": 1,
"day": 1
}
}>><<nobr>>
<div class="game-intro">
<h1>Vesper</h1>
<p class="game-description">An owned half-human weapon who feels nothing but the sex she's used for. Inside Vance Dynamics she's the company's slave; outside, under a false face, she slips into powerful men's lives and drains them while they think they're using her — sent to hunt a rogue called Cain.</p>
<div class="developer-intro">
<p class="developer-about">We're a small indie studio crafting intimate, story-driven experiences. Every game is made with care, and your support helps us keep creating. If you enjoy our work, consider supporting us!</p>
<p class="support-link">👉 <a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener">Support us on Patreon</a></p>
</div>
<div class="age-gate">
<p class="age-warning">⚠️ This game contains adult content intended for players 18 years of age or older.</p>
<div class="age-buttons">
[[✓ I am 18 or older - Enter Game->StartingCanvas_canvas_opening_office_Node_office]]
[[✗ I am NOT 18 or older->AgeBlocked]]
</div>
</div>
<div class="developer-footer">
<p class="developer-credit">Developed by <strong>NutGames</strong></p>
<p class="support-link">👉 <a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener">Support us on Patreon</a></p>
</div>
</div>
<</nobr>><div class="blocked-page">
<h2>Access Denied</h2>
<p>This content is not available for you.</p>
<p>Please close this page.</p>
</div><!-- Time Display Widgets -->
<<widget "timeFormatted">>
<<set _hour to $game_state.time_state.current_hour>>
<<set _minute to $game_state.time_state.current_minute>>
<<set _displayHour to (_hour is 0 ? 12 : (_hour > 12 ? _hour - 12 : _hour))>>
<<set _ampm to (_hour < 12 ? "AM" : "PM")>>
<<set _minuteStr to (_minute < 10 ? "0" + _minute : _minute)>>
<<print _displayHour + ":" + _minuteStr + " " + _ampm>>
<</widget>>
<!-- Render a quest card: narrative line + (Pattern 2) auto-rendered 🎯 goal
block + 💡 tip line. Author tags state-pressure variants in the TOML and
the picker swaps which line fires; widget renders the chosen line, then
calls setup.computeHintGoal() to render the structured progress block from
the hint's stage condition (or canvas trigger conditions for branch-inside-
shell transitions). Game-level mechanics (decay rates, time costs, etc.)
live on :: TipsPage instead. -->
<<widget "renderStageHint">>
/* Pattern 2 (2026-05-01): accepts either a string (legacy callers) OR a
hint object {text, condition, tip, auto_goal, ...}. When passed an object
with auto_goal=true and a stage condition, the engine computes the
structured 🎯 goal block (bullets + live progress + 📍 location). When
passed a string OR a no-auto-goal object, falls back to splitting on
" — 🎯 " (legacy behavior) so existing games keep working unchanged. */
<<set _hintArg to $args[0]>>
<<set _hintObj to (typeof _hintArg === "object" && _hintArg !== null) ? _hintArg : null>>
<<set _hintText to _hintObj ? (_hintObj.text || "") : (_hintArg || "")>>
<<set _computedGoalHtml to _hintObj ? setup.computeHintGoal(_hintObj) : "">>
<<set _separator to " — 🎯 ">>
<<set _splitIdx to _hintText.indexOf(_separator)>>
<<if _computedGoalHtml>>
/* New path: structured auto-render. If text contains " — 🎯 ", strip the
manual goal portion (auto-render replaces it). */
<<if _splitIdx gt -1>>
<<set _flavor to _hintText.substring(0, _splitIdx)>>
<<else>>
<<set _flavor to _hintText>>
<</if>>
/* 2026-05-09: when State C "🔓 Ready" fires AND the hint defines
ready_text, swap flavor → ready_text. The author-supplied ready_text
replaces the regular text once all helper gates are met. */
<<if _hintObj && _hintObj.ready_text && setup._isHintReady(_hintObj)>>
<<set _flavor to _hintObj.ready_text>>
<</if>>
<div class="stage-hint-card">
<div class="stage-hint-flavor"><<print _flavor>></div>
<<print _computedGoalHtml>>
<<if _hintObj && _hintObj.tip>>
<div class="stage-hint-tip">💡 <<print _hintObj.tip>></div>
<</if>>
</div>
<<elseif _splitIdx gt -1>>
/* Legacy path: split on " — 🎯 " */
<<set _flavor to _hintText.substring(0, _splitIdx)>>
<<set _goal to _hintText.substring(_splitIdx + _separator.length)>>
<div class="stage-hint-card">
<div class="stage-hint-flavor"><<print _flavor>></div>
<div class="stage-hint-goal"><span class="stage-hint-target">🎯</span> <<print _goal>></div>
<<if _hintObj && _hintObj.tip>>
<div class="stage-hint-tip">💡 <<print _hintObj.tip>></div>
<</if>>
</div>
<<elseif _hintText>>
<div class="stage-hint-card">
<div class="stage-hint-flavor"><<print _hintText>></div>
</div>
<</if>>
<</widget>>
<<widget "timeDisplay">>
<div id="time-widget" class="time-display">
<div class="time-line">
<span id="time-display"><<timeFormatted>></span> | <span id="current-day"><<print $game_state.time_state.current_day>></span>
</div>
<div class="control-line">
<button class="time-btn" onclick="waitTime(10)" title="Advance 10 minutes">></button> | <button class="time-btn" onclick="waitTime(60)" title="Advance 1 hour">>></button> | <button class="time-btn" onclick="waitTime(1440)" title="Advance 1 day">>>>>></button>
</div>
</div>
<</widget>>
<<widget "sidebarItems">>
<<if setup.sidebar_items && setup.sidebar_items.length > 0>>
<div id="sidebar-items-widget">
<<for _si to 0; _si lt setup.sidebar_items.length; _si++>>
<<set _item to setup.sidebar_items[_si]>>
<<if _item.show_when and not setup.triggerConditionsSatisfied(_item.show_when)>>
<<continue>>
<</if>>
<<if _item.type is "countdown">>
<<set _daysLeft to _item.total_days - $game_state.time_state.day + 1>>
<div class="sidebar-item countdown-item" id="sidebar-countdown-<<print _si>>">
<<if _daysLeft gt 1>>
<<print _daysLeft + " " + _item.label>>
<<elseif _daysLeft is 1>>
<<print "Tomorrow is " + _item.label.replace("days until ", "")>>
<<elseif _daysLeft is 0>>
<<print "Today is " + _item.label.replace("days until ", "")>>
<<else>>
<<print _item.label.replace("days until ", "") + " has passed">>
<</if>>
</div>
<<elseif _item.type is "quest_next">>
/* The next STEP, on every screen — as opposed to trait_status_text, which is the
next STATE. A game shipped with four band strings in the sidebar and no verb, no
place and no person anywhere in the always-visible chrome: a player who never
opened the Quests page had no direction at all. Renders the same goal block as
the Quests page and the npc_panel "next" row (setup.renderQuestsGoalBlock), so
there is one implementation of what an objective looks like, not three.
`npc_id` picks that character's live card; omitted, it takes the live tier cards
in file order. `max` caps how many render (default 3). Terminal cards are
skipped: they carry no goals and would render a bare completion badge forever. */
<<if setup.renderQuestsGoalBlock and setup.evaluateGoals>>
<<set _qnCards to []>>
<<if _item.npc_id>>
<<set _qnOne to setup.pickQuestsCard(_item.npc_id)>>
<<if _qnOne>><<run _qnCards.push(_qnOne)>><</if>>
<<else>>
<<set _qnCards to setup.pickQuestsCards("story_goals") || []>>
<</if>>
<<set _qnMax to _item.max || 3>>
<<set _qnShown to 0>>
<<for _qi to 0; _qi lt _qnCards.length; _qi++>>
<<set _qnCard to _qnCards[_qi]>>
<<if _qnShown lt _qnMax and _qnCard and _qnCard.goals and _qnCard.goals.length gt 0 and not _qnCard.terminal>>
<<set _qnBlock to setup.renderQuestsGoalBlock(_qnCard, setup.evaluateGoals(_qnCard))>>
<<if _qnBlock>>
<<set _qnShown to _qnShown + 1>>
<div class="sidebar-item quest-next-item" id="sidebar-questnext-<<print _si>>-<<print _qi>>">
<<print _qnBlock>>
</div>
<</if>>
<</if>>
<</for>>
<</if>>
<<elseif _item.type is "trait_bar">>
<<set _tbOwner to _item.trait_owner || "player">>
<<set _tbKey to _item.trait>>
<<if _tbOwner is "npc">>
<<set _tbNpcId to _item.npc_id>>
<<set _tbNpcObj to (setup.npc_slug_map && setup.npc_slug_map[_tbNpcId]) ? State.variables.npcs[setup.npc_slug_map[_tbNpcId]] : (State.variables.npcs ? State.variables.npcs[_tbNpcId] : null)>>
<<set _traitVal to (_tbNpcObj && _tbNpcObj.core_traits) ? (_tbNpcObj.core_traits[_tbKey] || 0) : 0>>
<<else>>
<<set _traitVal to ($player && $player.core_traits) ? ($player.core_traits[_tbKey] || 0) : 0>>
<</if>>
<<set _traitMax to _item.max || 100>>
<<set _traitLabel to _item.label || _tbKey>>
<<set _traitPct to Math.max(0, Math.min(100, (_traitVal / _traitMax) * 100))>>
<<set _tbTier to "">>
<<if _item.color_tiers>>
<<for _ti to 0; _ti lt _item.color_tiers.length; _ti++>>
<<if _tbTier is "" and _traitPct lte (_item.color_tiers[_ti].up_to)>>
<<set _tbTier to _item.color_tiers[_ti].class>>
<</if>>
<</for>>
<</if>>
<<set _tbBandText to "">>
<<set _tbBandIcon to "">>
<<if _item.bands>>
<<for _bi to 0; _bi lt _item.bands.length; _bi++>>
<<set _bb to _item.bands[_bi]>>
<<if _tbBandText is "" and _bb.min isnot undefined and _bb.max isnot undefined and _traitVal gte _bb.min and _traitVal lte _bb.max>>
<<set _tbBandText to _bb.text>>
<<set _tbBandIcon to _bb.icon || "">>
<</if>>
<</for>>
<</if>>
<div class="sidebar-item trait-bar-item" id="sidebar-trait-bar-<<print _si>>">
<div class="trait-bar-label">
<<if _item.hide_value is true>>
<<print _traitLabel>>
<<else>>
<<print _traitLabel>>: <<print Math.floor(_traitVal)>> / <<print _traitMax>>
<</if>>
</div>
<div class="trait-bar-bg">
<div class="trait-bar-fill <<print _tbTier>>" style="width: <<print _traitPct>>%"></div>
<<if _tbBandText isnot "">>
<span class="trait-bar-band-text"><<print (_tbBandIcon ? _tbBandIcon + " " : "") + _tbBandText>></span>
<</if>>
</div>
</div>
<<elseif _item.type is "trait_status_text">>
<<set _tsOwner to _item.trait_owner || "player">>
<<set _tsKey to _item.trait>>
<<if _tsOwner is "npc">>
<<set _tsNpcId to _item.npc_id>>
<<set _tsNpcObj to (setup.npc_slug_map && setup.npc_slug_map[_tsNpcId]) ? State.variables.npcs[setup.npc_slug_map[_tsNpcId]] : (State.variables.npcs ? State.variables.npcs[_tsNpcId] : null)>>
<<set _tsVal to (_tsNpcObj && _tsNpcObj.core_traits) ? (_tsNpcObj.core_traits[_tsKey] || 0) : 0>>
<<else>>
<<set _tsVal to ($player && $player.core_traits) ? ($player.core_traits[_tsKey] || 0) : 0>>
<</if>>
<<set _tsText to "">>
<<set _tsIcon to "">>
<<if _item.bands>>
<<for _bi to 0; _bi lt _item.bands.length; _bi++>>
<<set _bb to _item.bands[_bi]>>
<<set _bMin to (_bb.min isnot undefined) ? _bb.min : -1e9>>
<<set _bMax to (_bb.max isnot undefined) ? _bb.max : 1e9>>
<<if _tsText is "" and _tsVal gte _bMin and _tsVal lte _bMax>>
<<set _tsText to _bb.text>>
<<set _tsIcon to _bb.icon || "">>
<</if>>
<</for>>
<</if>>
<<if _tsText isnot "">>
<div class="sidebar-item trait-status-text-item">
<<if _item.label>><div class="band-header"><<print _item.label>></div><</if>>
<span class="band-value"><<print (_tsIcon ? _tsIcon + " " : "") + _tsText>></span>
</div>
<</if>>
<<elseif _item.type is "trait_decay_warning">>
<!-- E20: render an amber warning when a decaying trait dropped today AND
is within 2.0 of its next stage gate. setup.getDecayWarnings() walks
the configured thresholds and compares last_day_snapshot vs current. -->
<<set _decayWarnings to setup.getDecayWarnings(_item.thresholds || {})>>
<<for _dw range _decayWarnings>>
<div class="sidebar-item trait-decay-warning-item">
⚠ <<print _dw.text>>
</div>
<</for>>
<<elseif _item.type is "passes">>
<div class="sidebar-item passes-item" id="sidebar-passes-<<print _si>>">
<<for _pi to 0; _pi lt setup.passes.length; _pi++>>
<<set _pass to setup.passes[_pi]>>
<<set _remaining to setup.getPassDaysRemaining(_pass.id)>>
<<if _remaining gte 0>>
<div class="pass-entry pass-active" id="pass-display-<<print _pass.id>>">
<<print (_pass.icon || "") + " " + _pass.name + ": " + _remaining + "d">>
</div>
<</if>>
<</for>>
</div>
<<elseif _item.type is "inventory">>
<div class="sidebar-item inventory-item" id="sidebar-inventory-<<print _si>>">
<<for _ii to 0; _ii lt setup.items.length; _ii++>>
<<set _itm to setup.items[_ii]>>
<<set _count to setup.getItemCount(_itm.id)>>
<<if _count gt 0>>
<div class="inventory-entry" id="inventory-display-<<print _itm.id>>">
<<print (_itm.icon || "") + " " + _itm.name + ": " + _count>>
</div>
<</if>>
<</for>>
</div>
<<elseif _item.type is "trait_words">>
<<set _twOwner to _item.trait_owner || "player">>
<<set _twKey to _item.trait>>
<<if _twOwner is "npc">>
<<set _twNpcId to _item.npc_id>>
<<set _twNpcObj to (setup.npc_slug_map && setup.npc_slug_map[_twNpcId]) ? State.variables.npcs[setup.npc_slug_map[_twNpcId]] : (State.variables.npcs ? State.variables.npcs[_twNpcId] : null)>>
<<set _twVal to (_twNpcObj && _twNpcObj.core_traits) ? (_twNpcObj.core_traits[_twKey] || 0) : 0>>
<<else>>
<<set _twVal to ($player && $player.core_traits) ? ($player.core_traits[_twKey] || 0) : 0>>
<</if>>
<<set _twMatched to "">>
<<set _twFound to false>>
<<if _item.bands>>
<<for _bi to 0; _bi lt _item.bands.length; _bi++>>
<<if not _twFound>>
<<set _twBand to _item.bands[_bi]>>
<<if _twBand.flag>>
<<if $flags and $flags[_twBand.flag] is true>>
<<set _twMatched to _twBand.text>>
<<set _twFound to true>>
<</if>>
<<elseif _twBand.min isnot undefined and _twBand.max isnot undefined>>
<<if _twVal gte _twBand.min and _twVal lte _twBand.max>>
<<set _twMatched to _twBand.text>>
<<set _twFound to true>>
<</if>>
<</if>>
<</if>>
<</for>>
<</if>>
<<if _twMatched isnot "">>
<div class="sidebar-item trait-words-item" id="sidebar-trait-words-<<print _si>>">
<<if _item.label>><div class="band-header"><<print _item.label>></div><</if>>
<span class="band-value"><<print _twMatched>></span>
</div>
<</if>>
<<elseif _item.type is "stage_label">>
/* E11: render "<prefix>: <stage label>" sourced from arc_stages.
Stage value lives in $player.core_traits[<slug>_stage] (integer).
Out-of-range value → highest-defined label. Undefined trait → just
the prefix with no colon. Empty arc_stages → renders nothing. */
<<set _slNpcId to _item.npc_id>>
<<set _slStages to (setup.npc_arc_stages && setup.npc_arc_stages[_slNpcId]) ? setup.npc_arc_stages[_slNpcId] : []>>
<<set _slRawStage to ($player && $player.core_traits) ? $player.core_traits[_slNpcId + "_stage"] : undefined>>
<<set _slPrefix to _item.prefix>>
<<if not _slPrefix>>
<<set _slUuid to (setup.npc_slug_map && setup.npc_slug_map[_slNpcId]) ? setup.npc_slug_map[_slNpcId] : _slNpcId>>
<<set _slNpcObj to State.variables.npcs ? State.variables.npcs[_slUuid] : null>>
<<set _slPrefix to _slNpcObj ? (_slNpcObj.name || _slNpcId) : _slNpcId>>
<</if>>
<<if _slStages.length gt 0 and _slRawStage isnot undefined>>
<<set _slIdx to Math.max(0, Math.min(Number(_slRawStage), _slStages.length - 1))>>
<<set _slLabel to _slStages[_slIdx]>>
<div class="sidebar-item stage-label-item" id="sidebar-stage-label-<<print _si>>">
<<print _slPrefix>>: <<print _slLabel>>
</div>
<<elseif _slPrefix>>
<div class="sidebar-item stage-label-item" id="sidebar-stage-label-<<print _si>>">
<<print _slPrefix>>
</div>
<</if>>
<<elseif _item.type is "npc_panel">>
/* RTS-style per-NPC card: name header + rows (arousal band / corruption / location).
Location uses setup.getNpcLocation — the SAME schedule source the Schedule page uses. */
<<set _npId to _item.npc_id>>
<<set _npObj to (setup.npc_slug_map && setup.npc_slug_map[_npId]) ? State.variables.npcs[setup.npc_slug_map[_npId]] : (State.variables.npcs ? State.variables.npcs[_npId] : null)>>
<<if _npObj>>
<<set _npRows to _item.rows || ["arousal"]>>
<<set _npHeader to _item.label || _npObj.name || _npId>>
<div class="sidebar-item npc-panel-item" id="sidebar-npc-panel-<<print _si>>">
<div class="npc-panel-header"><<print _npHeader>></div>
<<for _npri to 0; _npri lt _npRows.length; _npri++>>
<<set _npRow to _npRows[_npri]>>
<<if _npRow is "arousal">>
<<if not (setup.hiddenTraits && setup.hiddenTraits.includes("arousal"))>>
<<set _npAr to (_npObj.core_traits) ? (_npObj.core_traits.arousal || 0) : 0>>
<<set _npBands to _item.arousal_bands || [{"min":0,"max":0,"text":"❄️"},{"min":1,"max":1,"text":"🔥"},{"min":2,"max":2,"text":"🔥🔥"},{"min":3,"max":3,"text":"🔥🔥🔥"}]>>
<<set _npArText to "">>
<<for _nbi to 0; _nbi lt _npBands.length; _nbi++>>
<<set _nb to _npBands[_nbi]>>
<<if _npArText is "" and _nb.min isnot undefined and _nb.max isnot undefined and _npAr gte _nb.min and _npAr lte _nb.max>>
<<set _npArText to _nb.text>>
<</if>>
<</for>>
<div class="sidebar-row"><span class="sidebar-label">🔥 Arousal</span> <span class="sidebar-value"><<print _npArText>></span></div>
<</if>>
<<elseif _npRow is "corruption">>
<<if not (setup.hiddenTraits && setup.hiddenTraits.includes("corruption"))>>
<<set _npCorr to (_npObj.core_traits) ? (_npObj.core_traits.corruption || 0) : 0>>
<<set _npCorrOut to _npCorr>>
<<if _item.corruption_max_value isnot undefined and _npCorr gte _item.corruption_max_value>>
<<set _npCorrOut to (_item.corruption_max_label || "MAX")>>
<</if>>
<div class="sidebar-row"><span class="sidebar-label">🫦 Corruption</span> <span class="sidebar-value"><<print _npCorrOut>></span></div>
<</if>>
<<elseif _npRow is "location">>
<<set _npLoc to setup.getNpcLocation(_npId)>>
<<set _npLocName to _npLoc ? (setup._locNameFromUuid(_npLoc.location) || _npLoc.location) : (_item.away_label || "Away")>>
<div class="sidebar-row"><span class="sidebar-label">📍 Location</span> <span class="sidebar-value"><<print _npLocName>></span></div>
<<elseif _npRow is "next">>
/* Next-milestone block — EXACT Quests-page parity: reuses setup.renderQuestsGoalBlock, so
the card shows the same goal block as the Quests page — 🎯 To advance + ◯ live progress
(climbing) / 🔓 Ready + 📍 + 🕒 (ready) / ✓ Arc complete (terminal). No flavor/tip text. */
<<if setup.pickQuestsCard and setup.renderQuestsGoalBlock>>
<<set _npCard to setup.pickQuestsCard(_npId)>>
<<if _npCard>>
<<set _npGoalBlock to setup.renderQuestsGoalBlock(_npCard, setup.evaluateGoals(_npCard))>>
<<if _npGoalBlock>>
<div class="npc-panel-next"><<print _npGoalBlock>></div>
<</if>>
<</if>>
<</if>>
<</if>>
<</for>>
</div>
<</if>>
<</if>>
<</for>>
</div>
<</if>>
<</widget>>
<<widget "playerPortrait">>
<<if setup.player_portrait_enabled and setup.player_portrait>>
<<set _pimg to setup.getPlayerPortrait()>>
<<if _pimg>>
<div class="sidebar-player-portrait"><img @src="_pimg" @alt="$player.name" onerror="this.style.display='none';"></div>
<</if>>
<</if>>
<</widget>>
<<widget "activeModifiers">>
<<if $game_state && $game_state.active_modifiers && Object.keys($game_state.active_modifiers).length gt 0>>
<div id="sidebar-modifiers-widget" class="traits-display">
<div class="traits-header">Active Effects</div>
<<set _modKeys to Object.keys($game_state.active_modifiers)>>
<<for _mi to 0; _mi lt _modKeys.length; _mi++>>
<<set _mod to $game_state.active_modifiers[_modKeys[_mi]]>>
<<set _hoursLeft to (_mod.expires_day - $game_state.time_state.day) * 24 + (_mod.expires_hour - $game_state.time_state.current_hour)>>
<div class="modifier-badge"><<print _mod.name>> (<<print _hoursLeft>>h)</div>
<</for>>
</div>
<</if>>
<</widget>>
<<widget "inc">>
<<set _incTrait to $args[0]>>
<<set _incBy to ($args[1] != null) ? Number($args[1]) : 1>>
<<script>>setup.applyAndNotifyTrait("player", null, _incTrait, "add", _incBy, false, null);<</script>>
<</widget>>
<<widget "dec">>
<<set _decTrait to $args[0]>>
<<set _decBy to ($args[1] != null) ? Number($args[1]) : 1>>
<<script>>setup.applyAndNotifyTrait("player", null, _decTrait, "add", -_decBy, false, null);<</script>>
<</widget>>
<<widget "playerTraits">>
<div id="traits-widget" class="traits-display">
<div class="traits-header">Traits</div>
<<if $player and $player.core_traits and Object.keys($player.core_traits).length > 0>>
<<set _keys to Object.keys($player.core_traits).sort()>>
<ul class="traits-list">
<<for _i to 0; _i lt _keys.length; _i++>>
<<set _k to _keys[_i]>>
<<if setup.hiddenTraits && setup.hiddenTraits.includes(_k)>><<continue>><</if>>
<li class="trait-item">
<span class="trait-name"><<print _k>></span>
<span class="trait-value"><<print $player.core_traits[_k]>></span>
</li>
<</for>>
</ul>
<<else>>
<div class="no-traits">No traits</div>
<</if>>
<div class="traits-hint">Updates after each choice.</div>
</div>
<</widget>>
<<widget "playerFlags">>
<div id="flags-widget" class="traits-display">
<div class="traits-header">Flags</div>
<<if $flags and Object.keys($flags).length > 0>>
<<set _fkeys to Object.keys($flags).sort()>>
<ul class="traits-list">
<<for _j to 0; _j lt _fkeys.length; _j++>>
<<set _fk to _fkeys[_j]>>
<li class="trait-item">
<span class="trait-name"><<print _fk>></span>
<span class="trait-value"><<print $flags[_fk] ? '✔' : '✖'>></span>
</li>
<</for>>
</ul>
<<else>>
<div class="no-traits">No flags</div>
<</if>>
<div class="traits-hint">Set by choices and story logic.</div>
</div>
<</widget>>
<<widget "questsButton">>
<!-- Debug/legacy quest view - shows raw mechanics -->
<div id="quests-btn-widget">
<<if passage() isnot "QuestsPage">><<link "Quests" "QuestsPage">><</link>><<else>><span class="nav-row">Quests</span><</if>><span class="nav-i">📋</span>
</div>
<</widget>>
<<widget "missingMediaButton">>
<<if $flags.debug_mode>>
<div id="missing-media-btn-widget" style="margin-bottom:8px;">
<<if passage() isnot "MissingMediaPage">><<link "⚠️ Missing Media" "MissingMediaPage">><</link>><<else>>⚠️ Missing Media<</if>>
</div>
<</if>>
<</widget>>
<<widget "journalButton">>
<!-- Quests - active per-NPC quest cards -->
<div id="journal-btn-widget">
<<if passage() isnot "QuestsPage">><<link "Quests" "QuestsPage">><</link>><<else>><span class="nav-row">Quests</span><</if>><span class="nav-i">📋</span>
</div>
<</widget>>
<<widget "tipsButton">>
<!-- Tips - game-level mechanics surface; conditional on [ui.tips_page] authored -->
<<if setup.tips_page && setup.tips_page.content>>
<div id="tips-btn-widget">
<<if passage() isnot "TipsPage">><<link "Tips" "TipsPage">><</link>><<else>><span class="nav-row">Tips</span><</if>><span class="nav-i">💡</span>
</div>
<</if>>
<</widget>>
<<widget "statsButton">>
<div id="stats-btn-widget">
<<if passage() isnot "StatsPage">><<link "Stats" "StatsPage">><</link>><<else>><span class="nav-row">Stats</span><</if>><span class="nav-i">📊</span>
</div>
<</widget>>
<<widget "scheduleButton">>
<<set _npcsWithSchedules to setup.getNpcsWithSchedules()>>
<<set _soloActivities to setup.getSoloActivitiesForToday()>>
<<if _npcsWithSchedules.length > 0 || _soloActivities.length > 0>>
<div id="schedule-btn-widget">
<<if passage() isnot "SchedulePage">><<link "Schedules" "SchedulePage">><</link>><<else>><span class="nav-row">Schedules</span><</if>><span class="nav-i">📅</span>
</div>
<</if>>
<</widget>>
<<widget "patreonButton">>
<div id="patreon-btn-widget">
<a href="https://www.patreon.com/cw/nutgames844" target="_blank" rel="noopener" class="patreon-link">
<svg class="patreon-icon" viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M15.386.524c-4.764 0-8.64 3.876-8.64 8.64 0 4.75 3.876 8.613 8.64 8.613 4.75 0 8.614-3.864 8.614-8.613C24 4.4 20.136.524 15.386.524M.003 23.537h4.22V.524H.003"/>
</svg>
Support Us
</a>
</div>
<</widget>>
<<widget "flagsButton">>
<div id="flags-btn-widget">
<<if passage() isnot "FlagsPage">><<link "Flags" "FlagsPage">><</link>><<else>><span class="nav-row">Flags</span><</if>><span class="nav-i">🚩</span>
</div>
<</widget>>
<<widget "versionFooter">>
<div class="sidebar-version">v0.2.0 · 2026-08-23</div>
<</widget>><<missingMediaButton>>
<<timeDisplay>>
<<playerPortrait>>
<<sidebarItems>>
<<activeModifiers>>
<<journalButton>>
<<tipsButton>>
<<cheatButton>>
<<statsButton>>
<<scheduleButton>>
<<playerTraits>>
<<patreonButton>>
<<versionFooter>>
<style>
#time-widget {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 4px;
padding: 8px;
margin-bottom: 10px;
font-family: var(--theme-font-mono);
font-size: 14px;
line-height: 1.2;
color: var(--theme-text);
}
.time-line {
text-align: center;
margin-bottom: 4px;
font-weight: bold;
color: var(--theme-text-strong);
}
.control-line {
text-align: center;
font-size: 12px;
}
.time-btn {
background: var(--theme-surface-alt);
border: 1px solid var(--theme-border);
border-radius: 3px;
color: var(--theme-text-secondary);
cursor: pointer;
padding: 3px 6px;
font-size: 12px;
font-family: var(--theme-font-mono);
font-weight: bold;
}
.time-btn:hover {
background: var(--theme-border);
color: var(--theme-text-strong);
border-color: var(--theme-border);
}
#sidebar-items-widget {
margin-bottom: 8px;
}
.sidebar-item {
text-align: left; /* one type system across the rail — left, theme sans (was centered mono) */
padding: 6px 8px;
font-size: 13px;
line-height: 1.3;
}
.countdown-item {
background: var(--theme-warning-bg);
border: 1px solid var(--theme-warning);
border-radius: 4px;
color: var(--theme-warning-text);
font-weight: bold;
}
.hint-item {
background: var(--theme-success-bg);
border: 1px solid var(--theme-success);
border-radius: 4px;
color: var(--theme-success);
font-style: italic;
}
/* quest_next — the next STEP in the rail. Quieter than the hint card on purpose: it
is there on every screen, so it reads as chrome rather than as an alert. The inner
goal block reuses the Quests-page classes (.quests-goal / .quests-ready / .quests-where). */
.quest-next-item {
padding: 4px 8px;
border-left: 2px solid var(--theme-border);
margin-bottom: 4px;
}
#traits-widget {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 4px;
padding: 8px;
margin-top: 8px;
font-family: var(--theme-font-mono);
font-size: 13px;
color: var(--theme-text);
}
#flags-widget {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 4px;
padding: 8px;
margin-top: 8px;
font-family: var(--theme-font-mono);
font-size: 13px;
color: var(--theme-text);
}
.traits-header {
font-weight: bold;
margin-bottom: 6px;
}
.traits-list {
list-style: none;
padding-left: 0;
margin: 0;
}
.trait-item {
display: flex;
justify-content: space-between;
border-bottom: 1px dashed var(--theme-border);
padding: 2px 0;
}
.trait-name {
color: var(--theme-text-secondary);
}
.trait-value {
color: var(--theme-primary);
font-weight: bold;
}
.no-traits {
font-style: italic;
color: var(--theme-text-muted);
}
.traits-hint {
margin-top: 6px;
font-size: 11px;
color: var(--theme-text-muted);
}
#time-display, #current-day {
font-family: var(--theme-font-mono);
color: var(--theme-text-strong);
}
/* ===== Sidebar action buttons — ONE neutral pill family =====
Structure is carried by neutral surfaces; the accent is reserved for hover
(and .is-active). Replaces the old zoo: 4 tinted pills + 2 parchment-serif
journal/tips. All six now share one filled surface pill, left-aligned, muted
label, accent on hover, single 6px radius. */
/* ===== Sidebar action buttons — ONE neutral pill family =====
The link (or the current-page .nav-row span) FILLS the whole padded box, so
the ENTIRE box is clickable — not just the text. The icon overlays the right
edge with pointer-events:none, so clicks there pass through to the link beneath.
Structure in neutrals; accent reserved for hover. */
#phone-sidebar-btn,
#quests-btn-widget,
#journal-btn-widget,
#tips-btn-widget,
#cheat-btn-widget,
#stats-btn-widget,
#schedule-btn-widget,
#flags-btn-widget {
position: relative; /* anchor the icon overlay + unread badge */
background: var(--theme-surface);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 6px;
margin: 8px 0 0 0; /* uniform 8px rhythm — same as the cards */
}
/* the clickable fill: <a> in nav state, .nav-row span on the current page */
#phone-sidebar-btn a,
#quests-btn-widget a, #journal-btn-widget a, #tips-btn-widget a, #cheat-btn-widget a, #stats-btn-widget a, #schedule-btn-widget a, #flags-btn-widget a,
#quests-btn-widget .nav-row, #journal-btn-widget .nav-row, #tips-btn-widget .nav-row, #cheat-btn-widget .nav-row, #stats-btn-widget .nav-row, #schedule-btn-widget .nav-row, #flags-btn-widget .nav-row {
display: block;
width: 100%;
box-sizing: border-box;
padding: 8px 30px 8px 10px; /* padded fill → whole box clickable; right room for the icon */
text-align: left; /* override the #ui-bar default center */
color: var(--theme-text-muted);
text-decoration: none;
font-weight: 600;
}
#phone-sidebar-btn a:hover,
#quests-btn-widget a:hover, #journal-btn-widget a:hover, #tips-btn-widget a:hover, #cheat-btn-widget a:hover, #stats-btn-widget a:hover, #schedule-btn-widget a:hover, #flags-btn-widget a:hover {
color: var(--theme-accent);
text-decoration: none;
}
.nav-i { /* right-edge icon overlay; clicks pass through to the link */
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
/* Sidebar spacing is margin-driven, not <br>-driven (kill stray empty-widget breaks). */
#story-caption br { display: none; }
/* State-reactive player portrait (opt-in) — the TOP-MOST sidebar image. The theme's
global img rule does not reach sidebar imgs, so without this the raw image renders at
natural size and overflows the narrow sidebar (you'd see a background edge, not the
face). Constrain to the sidebar width and frame it as a 3:4 portrait crop centred a bit
high, so a square or tall source shows the face/torso rather than the legs/background. */
.sidebar-player-portrait {
margin: 0 0 12px 0;
text-align: center;
}
.sidebar-player-portrait img {
display: block;
width: 100%;
max-width: 100%;
height: auto;
aspect-ratio: 3 / 4;
object-fit: cover;
object-position: 50% 18%;
border-radius: 6px;
}
/* Patreon Button in Sidebar */
#patreon-btn-widget {
margin: 15px 0 0 0;
padding: 15px 0 0 0;
border-top: 1px solid #444;
text-align: center;
}
/* Version / release-date footer at the very bottom of the sidebar */
.sidebar-version {
margin-top: 8px;
font-size: 11px;
color: var(--theme-text-muted, #8a8a8a);
text-align: center;
}
/* Cheat page rows. Flat and mechanical by design — this page is read as a list of
levers, not as prose, so the fiction lives on the container and the rows stay legible. */
.cheat-intro {
margin-bottom: 12px;
color: var(--theme-text-muted, #8a8a8a);
font-size: 13px;
}
.cheat-row {
padding: 7px 0;
border-bottom: 1px solid var(--theme-border, #2a2a2a);
}
.cheat-row:last-of-type { border-bottom: none; }
.cheat-hint, .cheat-at-cap {
margin-top: 2px;
color: var(--theme-text-muted, #8a8a8a);
font-size: 12px;
}
.cheat-row-maxed {
font-weight: 600;
opacity: 0.5;
}
/* Code entry. The box is the whole surface for a player with no code, so it is
sized to be obviously the thing to use rather than tucked in a corner. */
.cheat-entry {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.cheat-entry input[type="text"] {
flex: 1 1 160px;
min-width: 0;
padding: 6px 8px;
border: 1px solid var(--theme-border, #3a3a3a);
border-radius: 4px;
background: var(--theme-surface-alt, #1a1a1a);
color: var(--theme-text, #e8e8e8);
text-transform: uppercase;
letter-spacing: 0.08em;
}
#cheat-reply { flex: 1 1 100%; }
.cheat-reply-bad {
color: var(--theme-danger, #d06a6a);
font-size: 12px;
}
/* The join line is this page's only advertising: for a player with no code it is the
sole thing on the page that tells them what the box is for. */
.cheat-join {
margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px solid var(--theme-border, #2a2a2a);
color: var(--theme-text-muted, #8a8a8a);
font-size: 12px;
}
/* Version chip — the release a player matches against their guide, since codes are
scoped per release. Rides inside .sidebar-version and the cheat page's heading. */
.build-badge {
display: inline-block;
padding: 0 5px;
border: 1px solid var(--theme-border, #3a3a3a);
border-radius: 3px;
font-size: 10px;
letter-spacing: 0.02em;
white-space: nowrap;
color: var(--theme-text-muted, #8a8a8a);
}
/* Reduce gap between StoryCaption and Save/Restart menu */
#story-caption {
margin-bottom: 0 !important;
padding-bottom: 0 !important;
}
#menu-story {
margin-top: 8px !important;
}
.patreon-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: #f96854;
color: #fff !important;
border-radius: 4px;
text-decoration: none;
font-size: 0.9em;
font-weight: bold;
}
.patreon-link:hover {
background: #e85a47;
text-decoration: none !important;
}
.patreon-icon {
flex-shrink: 0;
}
.stats-card {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 6px;
padding: 12px;
margin-bottom: 12px;
}
.stats-name {
font-weight: bold;
font-size: 1.2em;
color: var(--theme-text);
margin-bottom: 8px;
border-bottom: 1px solid var(--theme-border);
padding-bottom: 6px;
}
.stats-traits {
margin-top: 8px;
}
.stats-trait-item {
display: flex;
justify-content: space-between;
padding: 4px 8px;
border-bottom: 1px dashed var(--theme-border);
color: var(--theme-text-strong);
}
.stats-trait-value {
font-weight: bold;
color: var(--theme-primary);
}
/* Story Arc Help Page Styles */
.chapter-context {
background: linear-gradient(135deg, var(--theme-surface) 0%, var(--theme-surface-alt) 100%);
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.chapter-name {
font-size: 1.1em;
color: var(--theme-text);
font-weight: bold;
}
.chapter-mood {
font-size: 0.9em;
color: var(--theme-text-muted);
font-style: italic;
}
.available-section h3 {
font-size: 1em;
color: var(--theme-text-secondary);
margin: 0 0 10px 0;
border-bottom: 1px solid var(--theme-border);
padding-bottom: 6px;
}
.help-card {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 6px;
padding: 12px 16px;
margin-bottom: 10px;
}
.node-name {
font-weight: bold;
color: var(--theme-text);
}
.node-hint {
color: var(--theme-text-muted);
font-size: 0.9em;
margin-top: 4px;
}
.narrative-hint {
background: var(--theme-warning-bg);
border: 1px solid var(--theme-warning);
border-radius: 6px;
padding: 12px;
margin: 16px 0;
}
.narrative-hint p {
margin: 0;
color: var(--theme-warning-text);
}
.progress-summary {
text-align: center;
color: var(--theme-text-muted);
font-size: 0.9em;
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--theme-border);
}
.no-quests-msg {
font-style: italic;
color: var(--theme-text-muted);
padding: 10px;
}
/* Quest Page - Simplified One Activity Per NPC */
.npc-section {
margin-bottom: 20px;
padding: 16px;
background: linear-gradient(135deg, var(--theme-surface) 0%, var(--theme-surface-alt) 100%);
border-radius: 10px;
border: 1px solid var(--theme-border);
}
.npc-name {
font-size: 1.2em;
color: var(--theme-text);
margin: 0 0 12px 0;
border-bottom: 2px solid var(--theme-primary);
padding-bottom: 8px;
font-weight: 600;
}
.guide-hint {
font-size: 0.85em;
color: #f0c040;
font-style: italic;
}
.quest-available {
color: var(--theme-success);
font-weight: 500;
padding: 10px 14px;
background: rgba(40, 167, 69, 0.1);
border-radius: 6px;
border-left: 4px solid var(--theme-success);
}
/* E16: Stage hint two-part card. Flavor on top, goal block below. */
.stage-hint-card {
padding: 12px 14px;
background: rgba(40, 167, 69, 0.08);
border-radius: 6px;
border-left: 4px solid var(--theme-success);
margin: 8px 0;
}
.stage-hint-flavor {
font-style: italic;
opacity: 0.75;
margin-bottom: 10px;
font-size: 0.95em;
}
/* Pattern 2 (2026-05-01) — auto-rendered structured goal block */
.stage-hint-goal-header {
font-weight: 600;
margin-bottom: 6px;
color: var(--theme-success);
}
.stage-hint-goal ul {
list-style: none;
padding-left: 8px;
margin: 4px 0;
}
.stage-hint-goal li {
padding: 3px 0;
font-family: var(--theme-font-mono, 'Courier New', monospace);
font-size: 0.92em;
line-height: 1.4;
}
.stage-hint-met {
color: var(--theme-success);
font-weight: 600;
display: inline-block;
width: 1.2em;
}
.stage-hint-unmet {
color: var(--theme-text-muted);
display: inline-block;
width: 1.2em;
}
.stage-hint-met-row {
color: var(--theme-success);
opacity: 0.9;
}
.stage-hint-unmet-row {
color: var(--theme-text);
opacity: 0.85;
}
.stage-hint-progress {
color: var(--theme-text-muted);
margin-left: 6px;
font-size: 0.88em;
}
.stage-hint-where {
margin-top: 8px;
font-size: 0.9em;
opacity: 0.85;
color: var(--theme-text);
font-style: normal;
}
.stage-hint-tip {
margin-top: 10px;
padding: 6px 10px;
background: rgba(255, 193, 7, 0.08);
border-left: 3px solid var(--theme-warning, #ffc107);
border-radius: 3px;
font-size: 0.9em;
line-height: 1.45;
opacity: 0.92;
}
.stage-hint-path {
margin-top: 6px;
padding: 6px 8px;
border-left: 2px solid var(--theme-accent, #4ecdc4);
background: rgba(78, 205, 196, 0.05);
}
.stage-hint-path strong {
color: var(--theme-accent, #4ecdc4);
font-size: 0.92em;
}
/* Pattern 2 (2026-05-04) — Ready frame: shown when every gate of a stage
transition's trigger evaluates true. Replaces the gate checklist with a
compact "go here, this time window" surface. */
.stage-hint-ready {
color: var(--theme-success);
font-weight: 700;
letter-spacing: 0.02em;
}
/* Pattern 2 (2026-05-09) — Arc-complete frame: shown when an author marks
a hint template `arc_complete = true` (terminal stage in the slice).
Quieter than Ready — reads as closure rather than an action prompt. */
.stage-hint-arc-complete {
color: var(--theme-text-muted, #888);
font-weight: 600;
font-style: italic;
letter-spacing: 0.02em;
}
.quest-locked {
color: var(--theme-warning-text);
font-weight: 500;
padding: 10px 14px;
background: rgba(255, 193, 7, 0.15);
border-radius: 6px;
border-left: 4px solid var(--theme-warning);
}
.quest-conditions {
color: var(--theme-danger);
font-weight: 500;
padding: 10px 14px;
background: rgba(220, 53, 69, 0.1);
border-radius: 6px;
border-left: 4px solid var(--theme-danger);
}
.quest-waiting {
color: var(--theme-warning-text);
font-weight: 500;
padding: 10px 14px;
background: rgba(255, 193, 7, 0.15);
border-radius: 6px;
border-left: 4px solid var(--theme-warning);
font-style: italic;
}
.dev-canvas-info {
background: var(--theme-primary);
color: white;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 10px;
font-size: 11px;
font-family: var(--theme-font-mono);
}
.quest-complete {
color: var(--theme-success);
font-style: italic;
padding: 10px 14px;
text-align: center;
}
.no-quests {
color: var(--theme-text-muted);
font-style: italic;
padding: 12px;
text-align: center;
}
/* Tips page — wrapped inside an .npc-section card frame on the page itself.
Section headers (<h3>) use the same accent treatment as .npc-name dividers
in the Quests page so the two pages read as the same family. */
.tips-page-content {
line-height: 1.55;
color: var(--theme-text);
}
.tips-page-content h3 {
font-size: 1.05em;
color: var(--theme-text);
margin: 18px 0 8px;
padding-bottom: 6px;
border-bottom: 2px solid var(--theme-primary);
font-weight: 600;
}
.tips-page-content h3:first-child {
margin-top: 0;
}
.tips-page-content p {
margin: 6px 0;
}
.tips-page-content strong {
color: var(--theme-text);
font-weight: 600;
}
/* Trait Requirement Link */
.trait-requirement-link {
color: var(--theme-danger);
text-decoration: underline;
cursor: pointer;
}
.trait-requirement-link:hover {
color: var(--theme-danger);
}
/* Trait Activities Modal Overlay */
.trait-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
/* Modal Container */
.trait-modal {
background: #fff;
border-radius: 12px;
max-width: 400px;
width: 90%;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
.trait-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
background: var(--theme-surface);
border-bottom: 1px solid var(--theme-border);
}
.trait-modal-header h3 {
margin: 0;
font-size: 1.1em;
color: #333;
}
.trait-modal-close {
font-size: 24px;
cursor: pointer;
color: var(--theme-text-muted);
line-height: 1;
}
.trait-modal-close:hover {
color: #333;
}
.trait-modal-progress {
padding: 12px 20px;
background: rgba(220, 53, 69, 0.1);
color: var(--theme-danger);
font-weight: 500;
text-align: center;
}
.trait-modal-body {
padding: 16px 20px;
max-height: 50vh;
overflow-y: auto;
}
/* Activity List */
.trait-activity-list {
list-style: none;
padding: 0;
margin: 0;
}
.trait-activity-list li {
padding: 12px;
border-bottom: 1px solid var(--theme-border);
}
.trait-activity-list li:last-child {
border-bottom: none;
}
.activity-name {
font-weight: 600;
color: #333;
}
.activity-bonus {
float: right;
color: var(--theme-success);
font-weight: 500;
}
.activity-hint {
margin-top: 4px;
color: var(--theme-text-muted);
font-size: 0.9em;
clear: both;
}
/* Effect Toast Notification */
.effect-toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.85);
color: var(--theme-success);
padding: 10px 20px;
border-radius: 6px;
font-size: 14px;
z-index: 10002;
white-space: nowrap;
}
/* S4 — Threshold-publisher toast (gated_action). Distinct from the green
effect-toast: amber/warning palette, italic, allows wrapping for the longer
in-character explanation, slightly higher z-index so it doesn't get hidden
behind the success toast when both fire on the same click. */
.effect-toast.notify-warning {
background: rgba(255, 152, 0, 0.95);
color: #1a1a1a;
border-left: 4px solid #c66900;
font-style: italic;
white-space: normal;
max-width: 480px;
text-align: left;
line-height: 1.4;
z-index: 10003;
}
/* Game Intro - Top Left Aligned */
.game-intro {
text-align: left;
}
/* Age Gate Section */
.age-gate {
background: var(--theme-warning-bg);
border: 1px solid var(--theme-warning);
border-radius: 8px;
padding: 20px;
margin: 20px 0;
text-align: center;
}
.age-warning {
font-weight: bold;
color: var(--theme-warning-text);
margin-bottom: 15px;
}
.age-buttons {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
}
/* Developer Intro Section (above age gate) */
.developer-intro {
text-align: left;
color: var(--theme-text-muted);
margin: 20px 0;
}
/* Developer Footer Section (below age gate) */
.developer-footer {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid var(--theme-border);
text-align: center;
color: var(--theme-text-muted);
font-size: 0.9em;
}
.developer-intro a,
.developer-footer a {
color: #f96854;
text-decoration: none;
}
.developer-intro a:hover,
.developer-footer a:hover {
text-decoration: underline;
}
.developer-about {
line-height: 1.5;
margin: 10px 0;
}
.support-link {
margin-top: 8px;
}
.developer-footer .support-link {
font-weight: bold;
}
/* Age Blocked Page */
.blocked-page {
text-align: center;
padding: 50px;
color: var(--theme-danger);
background: var(--theme-danger-bg);
border-radius: 8px;
margin: 50px auto;
max-width: 400px;
}
/* Override SugarCube default vertical centering */
#story {
align-items: flex-start !important;
justify-content: flex-start !important;
padding-top: 1em !important;
}
.passage {
margin-top: 0 !important;
padding-top: 0 !important;
}
/* New/Unvisited Canvas Highlight */
.new-canvas a {
color: var(--theme-success) !important;
font-weight: 600;
}
.new-canvas a::before {
content: "✨ ";
}
/* Unlocked conditional choice highlight (in passages) */
.unlocked-choice a, .unlocked-choice .link-internal {
color: var(--theme-warning) !important;
font-weight: 600;
}
.unlocked-choice a::before, .unlocked-choice .link-internal::before {
content: "🔓 ";
}
/* Locked choice (visible but not available — Mode A) */
.locked-choice {
color: var(--theme-text-muted);
font-style: italic;
cursor: not-allowed;
opacity: 0.6;
display: inline-block;
padding: 2px 0;
}
.locked-choice::before {
content: "🔒 ";
}
/* Rejection choice (clickable but leads to rejection — Mode B) */
.rejection-choice a, .rejection-choice .link-internal {
color: #b45555 !important;
font-style: italic;
}
.rejection-choice a::before, .rejection-choice .link-internal::before {
content: "⚠️ ";
}
/* Active modifier sidebar badge */
.modifier-badge {
display: inline-block;
background: rgba(168, 85, 247, 0.15);
color: #c084fc;
border: 1px solid rgba(168, 85, 247, 0.3);
border-radius: 12px;
padding: 2px 10px;
font-size: 0.8em;
font-style: italic;
margin: 2px 0;
}
/* Unlocked choice canvas link highlight (in location activity list) */
.unlocked-choice-canvas a {
color: var(--theme-warning) !important;
font-weight: 600;
}
.unlocked-choice-canvas a::before {
content: "🔓 ";
}
/* Navigation New Content Indicator */
.nav-new {
color: var(--theme-warning);
font-weight: bold;
font-size: 1.1em;
}
/* Navigation NPC Portrait Indicator */
.nav-npc-portrait {
width: 24px;
height: 24px;
border-radius: 50%;
object-fit: cover;
vertical-align: middle;
margin-left: 4px;
border: 2px solid var(--theme-border);
}
/* ===============================================
VISUAL NAVIGATION GRID
=============================================== */
.location-nav-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin: 8px 0;
}
@media (min-width: 768px) {
.location-nav-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 1024px) {
.location-nav-grid {
grid-template-columns: repeat(4, 1fr);
}
}
/* Location Card */
.location-card {
display: flex;
flex-direction: column;
background: #2a2a2a;
border-radius: 8px;
overflow: hidden;
text-decoration: none !important;
color: inherit !important;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.location-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
/* Card Image Area (5:2 aspect ratio - more compact) */
.location-card-image {
width: 100%;
padding-bottom: 40%; /* 5:2 ratio */
background-size: cover;
background-position: center;
position: relative;
background-color: #1a1a1a;
}
/* Placeholder silhouette for locations without images */
.location-card-placeholder {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #2d2d3a 0%, #1a1a2a 100%);
}
.location-card-placeholder svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60%;
height: 60%;
opacity: 0.5;
}
/* Card Content Area */
.location-card-content {
padding: 6px 8px;
display: flex;
flex-direction: column;
gap: 2px;
}
.location-card-name {
font-weight: 600;
font-size: 0.9em;
line-height: 1.2;
color: var(--theme-text);
}
/* Indicators Row */
.location-card-indicators {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
}
/* NEW badge (replaces ! indicator in visual mode) */
.nav-new-badge {
background: var(--theme-warning);
color: #000;
font-size: 0.65em;
font-weight: bold;
padding: 2px 5px;
border-radius: 3px;
text-transform: uppercase;
}
/* NPC portrait badges (smaller version for cards) */
.nav-npc-badge {
width: 20px;
height: 20px;
border-radius: 50%;
object-fit: cover;
border: 1px solid var(--theme-text-muted);
}
/* Locked destination card (lock-as-prose): shown but not clickable, with the reason */
.location-card-locked {
opacity: 0.6;
cursor: not-allowed;
filter: grayscale(0.4);
}
.location-card-locked .location-card-image {
filter: grayscale(0.6) brightness(0.85);
}
.nav-locked-reason {
font-size: 0.7em;
line-height: 1.25;
color: var(--theme-text-muted);
font-style: italic;
margin-top: 2px;
}
/* Text-mode locked link */
.nav-link-locked {
color: var(--theme-text-muted);
font-style: italic;
}
/* Travel-cost tag on a nav destination (neutral, informational) */
.nav-cost-tag {
font-size: 0.65em;
color: var(--theme-text-muted);
background: var(--theme-surface);
border: 1px solid var(--theme-border, rgba(255,255,255,0.12));
border-radius: 3px;
padding: 1px 5px;
margin-left: 4px;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
/* Exit Links Section (text-only, below grid) */
.location-nav-exits {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #444;
}
.location-nav-exits a {
color: var(--theme-text-muted);
}
.location-nav-exits a:hover {
color: #fff;
}
/* ===== NPC Portrait System (Location Interaction) ===== */
/* Grid of clickable NPC portraits shown at locations */
.location-npcs {
display: flex;
gap: 1.5rem;
justify-content: center;
padding: 1.5rem 0;
flex-wrap: wrap;
}
.npc-portrait-card {
text-align: center;
cursor: pointer;
transition: transform 0.2s ease;
position: relative;
}
.npc-portrait-card:hover {
transform: scale(1.08);
}
.npc-portrait-link {
text-decoration: none !important;
color: inherit !important;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.npc-portrait-img {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 3px solid var(--theme-text-muted);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.npc-portrait-card:hover .npc-portrait-img {
border-color: var(--theme-border);
box-shadow: 0 0 12px rgba(200, 200, 200, 0.2);
}
/* Initial letter fallback for missing portraits */
.npc-portrait-placeholder {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, #3a3a4a 0%, #2a2a3a 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
font-weight: 600;
color: var(--theme-border);
border: 3px solid var(--theme-text-muted);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.npc-portrait-card:hover .npc-portrait-placeholder {
border-color: var(--theme-border);
box-shadow: 0 0 12px rgba(200, 200, 200, 0.2);
}
.npc-portrait-name {
display: block;
font-size: 0.85rem;
color: var(--theme-text-muted);
line-height: 1.2;
}
/* .npc-badge kept as base class for .npc-cost-badge (cost-blocked portraits).
NEW/unlocked portrait border + badge classes removed 2026-05-25. */
.npc-badge {
position: absolute;
top: -4px;
right: -4px;
font-size: 0.6rem;
font-weight: bold;
padding: 2px 5px;
border-radius: 8px;
text-transform: uppercase;
}
/* Solo activity buttons (activities with no NPC) */
.location-solo-activities {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #333;
}
.solo-activity-btn {
display: inline-block;
padding: 0.4rem 1rem;
margin: 0.3rem 0;
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 4px;
color: var(--theme-text) !important;
text-decoration: none !important;
transition: background 0.2s ease;
}
.solo-activity-btn:hover {
background: var(--theme-surface-alt);
color: var(--theme-text-strong) !important;
}
/* ===== Cost-blocked states ===== */
/* Blocked NPC portraits (greyed out) */
.npc-portrait-blocked .npc-portrait-img,
.npc-portrait-blocked .npc-portrait-placeholder {
filter: grayscale(70%) brightness(0.6);
border-color: #444;
}
.npc-portrait-blocked .npc-portrait-name {
color: var(--theme-text-muted);
}
.npc-portrait-blocked:hover {
transform: scale(1.04);
}
/* Cost badge on portraits */
.npc-cost-badge {
background: var(--theme-danger);
color: #fff;
font-size: 0.55rem;
position: absolute;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
padding: 1px 6px;
border-radius: 8px;
top: auto;
right: auto;
}
/* Blocked solo activities */
.solo-activity-blocked {
opacity: 0.5;
}
.solo-activity-blocked:hover {
opacity: 0.7;
}
.solo-cost-tag {
font-size: 0.75em;
color: var(--theme-danger);
}
/* E21 — author-opt-in cooldown entry. Non-clickable dimmed text. */
.solo-activity-cooldown {
display: inline-block;
padding: 4px 8px;
margin-bottom: 2px;
color: var(--theme-border);
font-style: normal;
font-size: 0.9em;
opacity: 0.65;
}
.solo-activity-cooldown em {
font-size: 0.85em;
opacity: 0.85;
}
/* Cost-blocked passage message */
.cost-blocked-message {
text-align: center;
padding: 2rem;
color: var(--theme-text-muted);
font-style: italic;
}
.cost-blocked-message p {
margin-bottom: 1rem;
}
/* ===== E20: Sidebar decay warning ===== */
.trait-decay-warning-item {
margin-top: 0.5rem;
padding: 6px 10px;
background: rgba(255, 193, 7, 0.18);
border-left: 3px solid var(--theme-warning);
border-radius: 4px;
color: var(--theme-warning-text);
font-size: 0.78rem;
line-height: 1.35;
}
/* ===== Sidebar band cards (trait_words / trait_status_text / trait_bar) =====
ONE card language. Structure is carried by NEUTRAL surfaces + spacing; the
accent is RESERVED for signal (hover / .is-active), never the resting stripe.
trait_words is the HERO stat (corruption / identity) — raised surface + larger
value so it anchors the top of the rail instead of reading as a peer card. */
.trait-words-item,
.trait-status-text-item,
.trait-bar-item {
text-align: left;
margin-top: 0.5rem;
padding: 8px 10px;
background: var(--theme-surface);
border: 1px solid rgba(255, 255, 255, 0.07); /* hairline — survives across tiers, clears the eye (the old #333 border was ~1.3:1, invisible) */
border-left: 3px solid rgba(255, 255, 255, 0.10); /* neutral at rest */
border-radius: 6px;
color: var(--theme-text);
font-size: 0.78rem;
line-height: 1.35;
}
/* HERO: the identity word-state (corruption) reads as the top-of-rail anchor. */
.trait-words-item {
background: var(--theme-surface-hover); /* +0.08 lighter — the existing 'raised' tier */
}
.trait-words-item .band-value {
font-size: 1.05rem;
line-height: 1.25;
}
/* The accent/semantic left-stripe is a SCARCE SIGNAL (active / urgent / locked),
not decoration — a row earns it; toggled by emitting the class on that item. */
.sidebar-item.is-active { border-left-color: var(--theme-accent); }
.sidebar-item.is-urgent { border-left-color: var(--theme-warning); }
.sidebar-item.is-locked { border-left-color: var(--theme-danger); }
/* the label, rendered as a compact card header (matches .npc-panel-header) */
.band-header {
color: var(--theme-text-muted);
font-weight: bold;
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
margin-bottom: 4px;
}
/* the value line (word / status text) */
.band-value {
color: var(--theme-text-strong);
font-weight: 600;
font-size: 0.86rem;
font-variant-numeric: tabular-nums lining-nums; /* columns don't jitter as values change */
}
/* ===== trait_bar — banded word over a flat fill bar ===== */
.trait-bar-label {
color: var(--theme-text-muted);
font-weight: bold;
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
margin-bottom: 5px;
}
.trait-bar-bg {
height: 8px;
background: var(--theme-surface-alt);
border-radius: 6px;
overflow: hidden;
position: relative;
}
.trait-bar-fill {
height: 100%;
background: var(--theme-accent);
border-radius: 6px;
transition: width 0.3s ease;
}
/* Banded mode: when a trait_bar's band-text overlay is rendered, the bg grows
to fit the overlay text. Gated via :has() so non-banded bars stay at 8px. */
.trait-bar-bg:has(.trait-bar-band-text) {
height: 26px;
border-radius: 6px;
}
.trait-bar-band-text {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 9px;
font-size: 0.78rem;
font-weight: 600;
color: var(--theme-text-strong);
font-variant-numeric: tabular-nums lining-nums;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
pointer-events: none;
z-index: 2;
white-space: nowrap;
}
/* Built-in value-tier fills — flat & matte (set a tier class via color_tiers).
Authors can override by shipping their own .trait-bar-fill.<class> rules. */
.trait-bar-fill.low {
background: #3b82c4;
}
.trait-bar-fill.medium {
background: #d98a3a;
}
.trait-bar-fill.high {
background: #d65a5a;
}
/* NPC panel card (npc_panel sidebar item) — RTS House-card: header strip + label/value rows */
.npc-panel-item {
text-align: left;
padding: 0;
margin-top: 0.5rem;
background: var(--theme-surface);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 6px;
overflow: hidden;
}
.npc-panel-header {
padding: 5px 8px;
background: var(--theme-surface-alt);
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
color: var(--theme-text-strong);
font-weight: bold;
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.npc-panel-item .sidebar-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 8px;
font-size: 12px;
}
.npc-panel-item .sidebar-label {
color: var(--theme-text-muted);
white-space: nowrap;
}
.npc-panel-item .sidebar-value {
color: var(--theme-text-strong);
font-weight: 500;
text-align: right;
font-variant-numeric: tabular-nums lining-nums;
}
.npc-panel-next {
margin-top: 4px;
padding: 4px 8px 2px;
border-top: 1px solid rgba(255, 255, 255, 0.07);
}
/* the inner goal block reuses the Quests-page classes (.quests-goal/.quests-ready/.quests-where) */
/* Dev Mode Controls */
.dev-adj-btn {
background: var(--theme-text-muted);
color: white;
border: none;
border-radius: 3px;
width: 20px;
height: 20px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
padding: 0;
line-height: 1;
vertical-align: middle;
}
.dev-adj-btn:hover {
background: var(--theme-text-muted);
}
.trait-controls {
display: flex;
align-items: center;
gap: 4px;
}
.trait-controls .trait-value {
min-width: 24px;
text-align: center;
}
/* Portrait System */
.portrait {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
background: var(--theme-surface-alt);
display: flex;
align-items: center;
justify-content: center;
}
.portrait img {
width: 100%;
height: 100%;
object-fit: cover;
}
.portrait-placeholder svg {
width: 70%;
height: 70%;
}
/* Dialog Block with Portrait */
.dialog-block {
display: flex;
align-items: center;
gap: 12px;
margin: 10px 0;
padding: 10px;
border-radius: 8px;
color: var(--theme-text);
}
.dialog-player {
border-left: 4px solid var(--theme-primary);
background: var(--theme-primary-bg);
}
.dialog-player strong {
color: var(--theme-primary);
}
.dialog-npc {
border-left: 4px solid var(--theme-text-muted);
background: var(--theme-surface);
}
.dialog-npc strong {
color: var(--theme-text-secondary);
}
.dialog-content {
flex: 1;
}
/* Thought Bubble Block (S8) — interior monologue, paired with cascade beats */
.thought-bubble {
display: flex;
align-items: center;
gap: 12px;
margin: 8px 0 8px 24px;
padding: 8px 12px;
border-radius: 12px;
background: var(--theme-surface-alt);
color: var(--theme-text-secondary);
font-style: italic;
border-left: 4px dashed var(--theme-text-secondary);
}
.thought-bubble-player {
border-left-color: var(--theme-accent, var(--theme-text-primary));
}
.thought-bubble-content {
flex: 1;
}
/* Cascade Advance Links (S7) — visual cue distinct from choice exits */
.macro-linkreplace a {
display: inline-block;
margin-top: 1px;
padding: 2px 8px;
background: var(--theme-surface);
border: 1px solid var(--theme-accent, var(--theme-text-primary));
border-radius: 6px;
color: var(--theme-accent, var(--theme-text-primary));
text-decoration: none;
font-size: 0.95em;
line-height: 1.3;
}
.macro-linkreplace a:hover {
background: var(--theme-accent, var(--theme-text-primary));
color: var(--theme-bg, #fff);
}
/* Engine "no exits satisfied" dev diagnostic (2026-05-06) — fires when a
canvas's all-conditional exit_block has zero satisfied choices. Visible
only when $flags.debug_mode is true. The console.warn fires regardless. */
.engine-diag-no-exits {
background: #2a0a0a;
border: 2px solid #c0392b;
padding: 12px 16px;
margin: 16px 0;
border-radius: 4px;
color: #f5d8d6;
font-family: var(--theme-font-mono, monospace);
font-size: 12px;
}
.engine-diag-header {
font-weight: bold;
color: #ff6b6b;
margin-bottom: 8px;
}
.engine-diag-hint {
color: #f0c0bd;
margin-bottom: 12px;
font-style: italic;
}
.engine-diag-choice {
background: #1a0505;
padding: 8px 12px;
margin: 8px 0;
border-left: 3px solid #c0392b;
}
.engine-diag-choice-text { font-weight: bold; }
.engine-diag-choice-logic { font-size: 11px; color: #d4a8a4; margin-bottom: 4px; }
.engine-diag-items { margin: 4px 0 0 0; padding-left: 16px; list-style: none; }
.engine-diag-items li { margin: 2px 0; }
.engine-diag-pass { color: #6dcf6d; }
.engine-diag-fail { color: #ff8a85; }
/* Stats Card Portrait (larger) */
.stats-portrait {
width: 80px;
height: 100px;
border-radius: 8px;
overflow: hidden;
background: var(--theme-surface-alt);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.stats-portrait img {
width: 100%;
height: 100%;
object-fit: cover;
}
.stats-portrait svg {
width: 60%;
height: 60%;
}
/* Stats Card with Portrait Layout */
.stats-card-with-portrait {
display: flex;
gap: 12px;
align-items: flex-start;
}
.stats-info {
flex: 1;
min-width: 0;
}
/* Wardrobe Page */
.wardrobe-page {
margin: 12px 0;
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 10px;
padding: 16px;
}
.wardrobe-table {
width: 100%;
border-collapse: collapse;
}
.wardrobe-row {
border-bottom: 1px solid var(--theme-border);
}
.wardrobe-row:last-child {
border-bottom: none;
}
.wardrobe-row-disabled {
opacity: 0.45;
}
.wardrobe-slot-label {
padding: 12px 8px;
font-weight: 700;
color: #333;
width: 90px;
vertical-align: middle;
font-size: 0.85em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.wardrobe-slot-items {
padding: 10px 4px;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.wardrobe-item {
position: relative;
width: 76px;
height: 76px;
border: 2px solid var(--theme-border);
border-radius: 8px;
cursor: pointer;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}
.wardrobe-item:hover {
border-color: var(--theme-text-muted);
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
.wardrobe-item-equipped {
border-color: var(--theme-success);
background: var(--theme-success-bg);
box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25);
}
.wardrobe-item-equipped:hover {
border-color: var(--theme-success);
}
.wardrobe-item-locked {
opacity: 0.5;
cursor: not-allowed;
filter: grayscale(50%);
}
.wardrobe-item-locked:hover {
transform: none;
box-shadow: none;
border-color: var(--theme-border);
}
.wardrobe-item-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.wardrobe-item-text {
font-size: 0.8em;
text-align: center;
padding: 6px;
word-break: break-word;
color: #333;
line-height: 1.2;
}
.wardrobe-badge-equipped {
position: absolute;
top: 2px;
right: 2px;
background: var(--theme-success);
color: #fff;
font-size: 11px;
width: 18px;
height: 18px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.wardrobe-badge-locked {
position: absolute;
bottom: 2px;
right: 2px;
font-size: 13px;
}
.wardrobe-unequip-btn {
background: none;
border: 1px solid var(--theme-text-muted);
color: var(--theme-text-muted);
width: 28px;
height: 28px;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
transition: background 0.15s, color 0.15s;
}
.wardrobe-unequip-btn:hover {
background: var(--theme-danger);
border-color: var(--theme-danger);
color: #fff;
}
.wardrobe-disabled-hint,
.wardrobe-empty-hint {
color: var(--theme-text-muted);
font-style: italic;
font-size: 0.85em;
}
.entry-blocked {
color: var(--theme-danger);
font-weight: 500;
}
.entry-requirements {
color: var(--theme-warning-text);
background: var(--theme-warning-bg);
padding: 8px 12px;
border-radius: 6px;
font-size: 0.9em;
}
.entry-blocked-narrative {
font-style: italic;
color: var(--theme-text-muted);
line-height: 1.6;
margin: 12px 0;
}
#wardrobe-btn-widget button {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
color: #333;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
width: 100%;
font-size: 0.95em;
margin: 4px 0;
}
#wardrobe-btn-widget button:hover {
background: var(--theme-surface-alt);
border-color: var(--theme-border);
}
/* Shop page styles */
.shop-page {
margin: 12px 0;
}
.shop-money {
background: var(--theme-success-bg);
border: 1px solid var(--theme-success);
border-radius: 8px;
padding: 10px 16px;
font-size: 1.05em;
font-weight: 600;
color: var(--theme-success);
margin-bottom: 16px;
display: inline-block;
}
.shop-tier {
margin-bottom: 20px;
border: 1px solid var(--theme-border);
border-radius: 10px;
overflow: hidden;
background: var(--theme-surface);
}
.shop-tier-locked {
opacity: 0.55;
}
.shop-tier-header {
background: var(--theme-text-strong);
color: #fff;
padding: 10px 16px;
font-weight: 700;
font-size: 0.95em;
display: flex;
justify-content: space-between;
align-items: center;
}
.shop-tier-locked .shop-tier-header {
background: var(--theme-text-muted);
}
.shop-tier-req {
font-weight: 400;
font-size: 0.85em;
opacity: 0.8;
}
.shop-tier-locked-msg {
padding: 12px 16px;
color: var(--theme-warning-text);
background: var(--theme-warning-bg);
font-style: italic;
font-size: 0.9em;
text-align: center;
}
.shop-items {
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
}
.shop-slot-group {
margin-bottom: 4px;
}
.shop-slot-label {
font-weight: 700;
font-size: 0.8em;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--theme-text-secondary);
padding: 4px 0;
border-bottom: 1px solid var(--theme-surface-alt);
margin-bottom: 6px;
}
.shop-slot-items {
display: flex;
flex-direction: column;
gap: 6px;
}
.shop-item {
display: flex;
align-items: center;
gap: 12px;
background: #fff;
border: 1px solid var(--theme-border);
border-radius: 8px;
padding: 10px 14px;
transition: border-color 0.15s;
}
.shop-item-thumb {
width: 56px;
height: 56px;
border: 2px solid var(--theme-border);
border-radius: 8px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background: var(--theme-surface);
flex-shrink: 0;
}
.shop-item-thumb-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.shop-item-thumb-text {
font-size: 0.7em;
text-align: center;
padding: 4px;
word-break: break-word;
color: var(--theme-text-muted);
line-height: 1.2;
}
.shop-item:hover {
border-color: var(--theme-border);
}
.shop-item-owned {
border-color: var(--theme-success);
background: var(--theme-success-bg);
}
.shop-item-unaffordable {
opacity: 0.55;
}
.shop-item-info {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
min-width: 0;
}
.shop-item-name {
font-weight: 600;
color: var(--theme-text);
font-size: 0.95em;
}
.shop-item-action {
display: flex;
align-items: center;
gap: 10px;
}
.shop-item-price {
font-weight: 600;
color: var(--theme-text-secondary);
font-size: 0.95em;
}
.shop-item-owned-badge {
color: var(--theme-success);
font-weight: 600;
font-size: 0.9em;
}
.shop-item-cant-afford {
color: var(--theme-danger);
font-size: 0.85em;
font-style: italic;
}
.shop-buy-btn {
background: var(--theme-primary);
color: #fff;
border: none;
padding: 6px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9em;
font-weight: 600;
transition: background 0.15s;
}
.shop-buy-btn:hover {
background: var(--theme-primary);
}
/* Rent page styles */
.rent-title {
color: var(--theme-bg);
margin-bottom: 16px;
font-size: 1.4em;
}
.rent-choices {
margin: 16px 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.rent-choices a {
display: block;
padding: 10px 16px;
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 6px;
color: var(--theme-text);
text-decoration: none;
font-weight: 500;
transition: background 0.15s, border-color 0.15s;
}
.rent-choices a:hover {
background: var(--theme-surface-alt);
border-color: var(--theme-border);
}
.rent-balance {
margin-top: 12px;
padding: 8px 12px;
background: rgba(0,0,0,0.04);
border-radius: 6px;
font-size: 0.95em;
}
.game-over-text {
text-align: center;
font-size: 1.5em;
font-weight: bold;
color: var(--theme-danger);
margin: 20px 0;
letter-spacing: 2px;
}
</style><<widget "renderQuestsCard">>
<<set _card to $args[0]>>
<<if _card>>
<<set _goalState to setup.evaluateGoals(_card)>>
<<set _flavor to (_goalState.allMet && _card.ready_text) ? _card.ready_text : _card.text>>
<<set _goalBlock to setup.renderQuestsGoalBlock(_card, _goalState)>>
<div class="quests-card">
<div class="quests-flavor"><<print _flavor>></div>
<<if _goalBlock>><<print _goalBlock>><</if>>
<<if _card.tip>><div class="quests-tip">💡 <<print _card.tip>></div><</if>>
</div>
<</if>>
<</widget>><<nobr>>
<h2>What's Next</h2>
/* PRD 48 — Quests Engine V2 page. One card per arc NPC + Story Goals
section. Picker swaps cards as state crosses; goal-block frame swaps
from 🎯 (climbing) → 🔓 (ready) → ✓ (terminal) automatically. */
<<set _goals = setup.pickQuestsCards("story_goals")>>
<<if _goals.length > 0>>
<div class="quests-section">
<h3>Story Goals</h3>
<<for _g range _goals>><<renderQuestsCard _g>><</for>>
</div>
<</if>>
/* The NPC sections come from the CARDS, not from setup.help_data.npcs. help_data.npcs is
populated only by canvases carrying targetType="npc" trait effects (_build_help_data), so an
NPC with a full quest ladder but no stat bumps — one whose arc runs on player counters — was
silently absent and its cards never rendered (Vesper's Colm: 4 cards, 0 renders). quests_cards
IS the source of truth for who has cards, and pickQuestsCard() matches card.npc_id directly,
so no slug/uuid translation is needed and this behaves the same in --use-db builds. */
<<set _allCards = setup.quests_cards || []>>
<<set _hidden = setup.hiddenNpcs || {}>>
<<set _slugMap = setup.npc_slug_map || {}>>
<<set _npcSlugs = []>>
<<for _c range _allCards>>
<<if _c && _c.npc_id && _npcSlugs.indexOf(_c.npc_id) === -1>>
/* hiddenNpcs is UUID-keyed (hidden_npcs_map); check both forms so hidden_from_ui still
suppresses a section whichever build mode this is. */
<<if !_hidden[_c.npc_id] && !_hidden[_slugMap[_c.npc_id]]>>
<<run _npcSlugs.push(_c.npc_id)>>
<</if>>
<</if>>
<</for>>
<<set _rendered = 0>>
<<for _slug range _npcSlugs>>
<<set _card = setup.pickQuestsCard(_slug)>>
<<if _card>>
<<set _nid = _slugMap[_slug] || _slug>>
<<set _rendered = _rendered + 1>>
<div class="quests-section">
<h3><<print ($npcs[_nid] && $npcs[_nid].name) || ($npcs[_slug] && $npcs[_slug].name) || _slug.replace("npc_", "")>></h3>
<<renderQuestsCard _card>>
</div>
<</if>>
<</for>>
/* "did we draw a card", not "does help_data list anybody" — the old test could leave a bare
Story-Goals heading with nothing under it. */
<<if _goals.length === 0 && _rendered === 0>>
<div class="no-quests">No active quests.</div>
<</if>>
<</nobr>>
<<link "← Back">><<run setup.smartBack()>><</link>><<nobr>>
<<set _tp = setup.tips_page || {}>>
<h2><<print _tp.title || "Tips">></h2>
<div class="npc-section">
<div class="tips-page-content">
<<print _tp.content || "No tips available.">>
</div>
</div>
<</nobr>>
<<link "← Back">><<run setup.smartBack()>><</link>><<set _position = setup.detectStoryPosition()>>
<<set _hint = setup.generateNarrativeHint()>>
<div class="journal-container">
<div class="journal-header">
<<if _position.current_chapter>>
<h2 class="journal-chapter-title"><<print _position.current_chapter.name>></h2>
<<if _position.current_chapter.mood>>
<div class="journal-chapter-mood <<print _position.current_chapter.mood>>">
<<print _position.current_chapter.description || "">>
</div>
<</if>>
<<else>>
<h2 class="journal-chapter-title">Your Story</h2>
<</if>>
<<if _position.progress_percent > 0>>
<div class="journal-progress">
<div class="journal-progress-bar">
<div class="journal-progress-fill" style="width: <<print _position.progress_percent>>%;"></div>
</div>
</div>
<</if>>
</div>
<!-- Memories (completed story moments) -->
<<if _position.completed_nodes.length > 0>>
<div class="journal-section">
<div class="journal-section-title">Memories</div>
<<for _i, _node range _position.completed_nodes>>
<div class="journal-memory <<if _node.is_milestone>>milestone<</if>>">
<div class="journal-memory-title"><<print _node.name>></div>
<<if _node.journal_entry>>
<<print _node.journal_entry>>
<</if>>
</div>
<</for>>
</div>
<</if>>
<!-- Active story threads (groups in progress) -->
<<if _position.active_groups.length > 0>>
<<set _incompleteGroups = _position.active_groups.filter(function(g) { return !g.isComplete && g.completed > 0; })>>
<<if _incompleteGroups.length > 0>>
<div class="journal-section">
<div class="journal-section-title">Unfolding</div>
<<for _i, _group range _incompleteGroups>>
<div class="journal-group">
<div class="journal-group-name"><<print _group.name>></div>
<div class="journal-group-progress">
<<print _group.description>>
</div>
</div>
<</for>>
</div>
<</if>>
<</if>>
<!-- Relationship states (NPC emotions) -->
<<if Object.keys($npcs).length > 0>>
<div class="journal-section">
<div class="journal-section-title">Connections</div>
<<for _npcId range Object.keys($npcs)>>
<<set _state = setup.interpretNpcState(_npcId)>>
<<if _state.npc_name>>
<div class="journal-npc">
<div class="journal-npc-name"><<print _state.npc_name>></div>
<div class="journal-npc-emotion"><<print _state.description>></div>
<<if _state.relationship_summary && _state.relationship_summary !== _state.description>>
<div class="journal-npc-summary"><<print _state.relationship_summary>></div>
<</if>>
</div>
<</if>>
<</for>>
</div>
<</if>>
<!-- Subtle narrative hint (only when needed) -->
<<if _hint.hint_type !== "none" && _hint.text>>
<div class="journal-hint <<print _hint.hint_type>>">
<<print _hint.text>>
</div>
<</if>>
<!-- Empty state -->
<<if _position.completed_nodes.length === 0 && Object.keys($npcs).length === 0>>
<div class="journal-empty">
Your story is just beginning...
</div>
<</if>>
<div class="journal-back-link">
[[Return to your story->Navigation]]
</div>
</div><<nobr>>
<h2>All Stats</h2>
<!-- Player Stats -->
<div class="stats-card stats-card-with-portrait">
<div class="stats-portrait">
<<if $player.portrait>>
<img @src="'./videos/' + $player.portrait" @alt="$player.name" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';">
<<else>>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>
<</if>>
</div>
<div class="stats-info">
<div class="stats-name">You (<<print $player.name>>)</div>
<div class="stats-traits">
<<if $player && $player.core_traits && Object.keys($player.core_traits).length > 0>>
<<for _tk, _tv range $player.core_traits>>
<<if setup.hiddenTraits && setup.hiddenTraits.includes(_tk)>><<continue>><</if>>
<div class="stats-trait-item">
<span><<print _tk>></span>
<span class="stats-trait-value"><<print _tv>></span>
</div>
<</for>>
<<else>>
<em>No traits</em>
<</if>>
</div>
</div>
</div>
<!-- NPC Stats -->
<<if Object.keys($npcs).length > 0>>
<<for _npcId, _npc range $npcs>>
<<if (setup.hiddenNpcs && setup.hiddenNpcs[_npcId])>><<continue>><</if>>
<div class="stats-card stats-card-with-portrait">
<div class="stats-portrait">
<<if _npc.portrait>>
<img @src="'./videos/' + _npc.portrait" @alt="_npc.name" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';">
<<else>>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>
<</if>>
</div>
<div class="stats-info">
<div class="stats-name"><<print _npc.name>></div>
<div class="stats-traits">
<<if _npc.core_traits && Object.keys(_npc.core_traits).length > 0>>
<<for _tk, _tv range _npc.core_traits>>
<<if setup.hiddenTraits && setup.hiddenTraits.includes(_tk)>><<continue>><</if>>
<div class="stats-trait-item">
<span><<print _tk>></span>
<span class="stats-trait-value"><<print _tv>></span>
</div>
<</for>>
<<else>>
<em>No traits</em>
<</if>>
</div>
</div>
</div>
<</for>>
<<else>>
<p><em>No NPCs found.</em></p>
<</if>>
<</nobr>>
<<link "← Back">><<run setup.smartBack()>><</link>><<nobr>>
<h2>Change Clothes</h2>
<<= setup.renderWardrobePage()>>
<</nobr>>
<div id="wardrobe-warning" style="display:none; color:#ff6b6b; margin:10px 0; padding:8px; border:1px solid #ff6b6b; border-radius:4px;"></div>
<<link "← Back">><<script>>
var dest = State.variables.last_game_passage || "Navigation";
var clothingMsg = setup.checkLocationClothing(dest);
if (clothingMsg) {
var el = document.getElementById('wardrobe-warning');
if (el) { el.innerHTML = clothingMsg; el.style.display = 'block'; }
} else {
setup.smartBack();
}
<</script>><</link>><h2>Not Dressed for This</h2>
<p><<print State.variables._clothing_block_message || "You need to put on more clothes.">></p>
<div class="clothing-block-choices">
<<link "Change clothes">><<script>>
State.variables.last_game_passage = State.variables._clothing_block_destination;
Engine.play("WardrobePage");
<</script>><</link>>
<br>
<<link "Go back">><<script>>
Engine.play(State.variables.last_game_passage || "Navigation");
<</script>><</link>>
</div><!-- LOCK-AWARENESS (2026-08-11). The Schedules page is a GUIDANCE surface: it is the only screen that
publishes NPC hours, so the player uses it to decide where to walk. Schedule rows carry no conditions
by design — the resolver reads exactly five keys and drops the rest — so a row keeps pointing at a
location long after the story seals it, and getNpcLocation happily resolves "NOW" to a building the
player can no longer enter. Measured on vesper: after the Act-1a close it advertised Mercer as NOW at
his penthouse for 15 hours of every 24, and Calloway and Vane at Vance Securities, all three sealed by
the same flag. That is worse than saying nothing, because the player walks there.
The fix is on the PAGE, never on the rows: navDestUnlocked() is a pure entry_conditions check on the
location (no current-location dependency, no side effects), so it answers "can she get in right now"
for any slug. Locked rows are muted and show navDestBlockedReason() instead of the activity, and the
NOW badge and its activity line are suppressed entirely rather than naming a door that will not open. -->
<<nobr>>
<<set _currentDay to $game_state.time_state.current_day>>
<<set _todayIndex to ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].indexOf(_currentDay)>>
<h2>📅 Weekly Schedule</h2>
<p class="current-time">Today is <<print _currentDay>> · <<print setup.formatTime($game_state.time_state.current_hour, $game_state.time_state.current_minute)>></p>
<<set _npcsWithSchedules to setup.getNpcsWithSchedules()>>
<<if _npcsWithSchedules.length === 0>>
<p><em>No NPCs have schedules defined.</em></p>
<<else>>
<<for _npcInfo range _npcsWithSchedules>>
<<set _npcId to _npcInfo.id>>
<<set _npcName to _npcInfo.name>>
<<set _currentLoc to setup.getNpcLocation(_npcId)>>
<<set _allSchedules to setup.getNpcAllSchedulesSorted(_npcId)>>
<div class="npc-section">
<h3 class="npc-name">
<<print _npcName>>
<<set _nowSlug to _currentLoc ? (setup._getLocUuidToSlug()[_currentLoc.location] || _currentLoc.location) : "">>
<<set _nowOpen to _currentLoc ? setup.navDestUnlocked(_nowSlug) : false>>
<<if _currentLoc and _nowOpen>>
<<set _locName to (setup.locations[_currentLoc.location] && setup.locations[_currentLoc.location].name) || setup._locNameFromUuid(_currentLoc.location) || _currentLoc.location>>
<span class="now-badge">NOW: <<print _locName>></span>
<</if>>
</h3>
<<if _currentLoc and _nowOpen and _currentLoc.activity>>
<div class="current-activity">"<<print _currentLoc.activity>>"</div>
<</if>>
<<if _allSchedules.length === 0>>
<p><em>No schedule entries.</em></p>
<<else>>
<table class="schedule-table">
<thead><tr><th>Time</th><th>Location</th><th>Activity</th><th>Days</th></tr></thead>
<tbody>
<<for _sch range _allSchedules>>
<<set _schSlug to _sch.location_slug || _sch.location>>
<<set _schOpen to setup.navDestUnlocked(_schSlug)>>
<<set _isCurrent to _schOpen && setup.isCurrentTimeSlot(_sch.start_time, _sch.end_time) && setup._weekdayMatches(_sch.weekdays, _todayIndex)>>
<<set _rowClass to _isCurrent ? "current-slot" : (_schOpen ? "" : "locked-slot")>>
<<set _schLocName to (setup.locations[_sch.location] && setup.locations[_sch.location].name) || _sch.location>>
<!-- @class, NOT class="<<print>>". SugarCube does not evaluate macros inside a raw HTML attribute — it
emits them verbatim, so the row shipped with the literal string as its class and `.current-slot`
never matched anything. Found 2026-08-11 while adding `.locked-slot`; the attribute directive is the
same one the choice renderer already uses for `unlocked-choice`. -->
<tr @class="_rowClass">
<td><<if _isCurrent>>▶ <</if>><<print _sch.start_time>>-<<print _sch.end_time || "?">></td>
<td><<print _schLocName>></td>
<td><<if _schOpen>><<print _sch.activity>><<else>><span class="locked-slot-reason"><<print setup.navDestBlockedReason(_schSlug)>></span><</if>></td>
<td><<print setup.renderWeekdayBadges(_sch.weekdays, _todayIndex)>></td>
</tr>
<</for>>
</tbody>
</table>
<</if>>
</div>
<</for>>
<</if>>
<<set _soloActivities to setup.getSoloActivitiesAllSchedules()>>
<<if _soloActivities.length > 0>>
<div class="npc-section solo-activities-section">
<h3 class="npc-name">Your Activities</h3>
<table class="schedule-table">
<thead><tr><th>Time</th><th>Location</th><th>Activity</th><th>Days</th></tr></thead>
<tbody>
<<for _act range _soloActivities>>
<<set _rowClass to _act.isCurrent ? "current-slot" : "">>
<!-- @class for the same reason as the NPC table above — the literal-attribute bug applied here too. -->
<tr @class="_rowClass">
<td><<if _act.isCurrent>>▶ <</if>><<print _act.startTime>>-<<print _act.endTime || "?">></td>
<td><<print _act.locationName>></td>
<td><<print _act.name>></td>
<td><<print setup.renderWeekdayBadges(_act.weekdays, _todayIndex)>></td>
</tr>
<</for>>
</tbody>
</table>
</div>
<</if>>
<<link "← Back">><<run setup.smartBack()>><</link>>
<</nobr>>
<style>
.current-time {
color: var(--theme-text-strong);
font-size: 0.9em;
margin: 0 0 10px 0;
}
.now-badge {
background: var(--theme-success);
color: white;
padding: 3px 8px;
border-radius: 10px;
font-size: 0.75em;
font-weight: bold;
margin-left: 10px;
vertical-align: middle;
}
.current-activity {
color: var(--theme-text-muted);
font-style: italic;
margin-bottom: 8px;
font-size: 0.85em;
}
.schedule-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85em;
color: var(--theme-text);
}
.schedule-table th {
text-align: left;
padding: 4px 8px;
background: var(--theme-surface);
border-bottom: 2px solid var(--theme-border);
color: var(--theme-text-strong);
font-weight: 600;
}
.schedule-table td {
padding: 4px 8px;
border-bottom: 1px solid var(--theme-border);
color: var(--theme-text-strong);
vertical-align: middle;
}
.schedule-table tr.current-slot {
background: var(--theme-warning-bg);
font-weight: 500;
}
.schedule-table tr.current-slot td:first-child {
color: var(--theme-warning-text);
}
/* A schedule row whose location the player cannot currently enter. Muted rather than hidden: a row that
vanishes reads as "he has no schedule there", where a muted one reads as "that door is shut for now",
which is the truth and is what a returning player needs. Never carries the ▶ current marker. */
.schedule-table tr.locked-slot {
opacity: 0.55;
}
.schedule-table tr.locked-slot .locked-slot-reason {
font-style: italic;
opacity: 0.85;
}
.solo-activities-section {
margin-top: 18px;
}
.weekday-badges {
display: inline-flex;
flex-wrap: wrap;
gap: 3px;
}
.weekday-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 8px;
font-size: 0.72em;
font-weight: 600;
background: var(--theme-surface);
color: var(--theme-text-muted);
border: 1px solid var(--theme-border);
}
.weekday-badge.weekday-today {
background: var(--theme-primary);
color: white;
border-color: var(--theme-primary);
}
.weekday-badge.weekday-all {
background: var(--theme-surface-alt);
color: var(--theme-text);
border-style: dashed;
}
</style><<nobr>>
<h2>🚩 Game Flags</h2>
<div class="flags-container">
<<if $flags && Object.keys($flags).length > 0>>
<div class="flags-section">
<h3>Story Flags</h3>
<<set _gkeys to Object.keys($flags).sort()>>
<ul class="flags-list">
<<for _i to 0; _i lt _gkeys.length; _i++>>
<<set _gk to _gkeys[_i]>>
<li class="flag-item">
<span class="flag-name"><<print _gk>></span>
<span class="flag-value <<print $flags[_gk] ? 'flag-true' : 'flag-false'>>"><<print $flags[_gk] ? '✔' : '✖'>></span>
</li>
<</for>>
</ul>
</div>
<<else>>
<div class="no-flags">No story flags set.</div>
<</if>>
</div>
<<link "← Back">><<run setup.smartBack()>><</link>>
<</nobr>>
<style>
.flags-container {
background: var(--theme-surface);
border: 1px solid var(--theme-border);
border-radius: 8px;
padding: 16px;
margin: 16px 0;
}
.flags-section {
margin-bottom: 20px;
}
.flags-section:last-child {
margin-bottom: 0;
}
.flags-section h3 {
color: var(--theme-text-secondary);
border-bottom: 2px solid var(--theme-border);
padding-bottom: 8px;
margin: 0 0 12px 0;
}
.flags-list {
list-style: none;
padding: 0;
margin: 0;
}
.flag-item {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid var(--theme-surface-alt);
}
.flag-item:last-child {
border-bottom: none;
}
.flag-name {
font-weight: 500;
color: var(--theme-text);
}
.flag-value {
font-family: var(--theme-font-mono);
}
.flag-true {
color: var(--theme-text-strong);
}
.flag-false {
color: var(--theme-text-strong);
}
.no-flags {
color: var(--theme-text-muted);
font-style: italic;
padding: 12px;
background: #fff;
border-radius: 4px;
}
</style><img src="./videos/scenes/opening_office.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Four of them stand in a line on the black floor of Mercer's office. Vega, Lyra, Nova — chrome-perfect, hands at their sides, nothing moving behind their eyes. And Wren, at the end, the one that came back wrong.</p><span id="cascade-b2-beat-0"><p>Mercer doesn't look up from the desk. He lets the quiet run until it has weight; he has all night, and the units were built to stand here as long as he wants them to.</p><span id="cascade-b2-beat-1"><<linkreplace "Hear it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You had him. Cain. In the open, in your sights — and you stood there and let him walk.</div></div><p>She had. Her hand had been on the trigger and her hand had not closed, and there is no file for why. She has run the diagnostic since. It comes back clean, and clean is the wrong answer.</p><span id="cascade-b2-beat-2"><<linkreplace "Take it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> An asset that won't fire is scrap. I could have you stripped for parts by morning and the Chairman wouldn't blink.</div></div><p>The units don't move. They've watched him scrap things before.</p><<link "Beg him to keep you." "StartingCanvas_canvas_opening_office_Node_punishment">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Say nothing." "StartingCanvas_canvas_opening_office_Node_punishment">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Tell him, flatly, that you froze." "StartingCanvas_canvas_opening_office_Node_punishment">><<script>>advanceTime(3);<</script>><</link>><br>
<</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "canvas_opening_office">>
<<set $game_state.current_node = "canvas_opening_office.office">>
<<script>>setup.markCanvasTriggered("canvas_opening_office");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_opening_office.office") === -1) { vn.push("canvas_opening_office.office"); }<</script>><img src="./videos/scenes/opening_punishment.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Whatever she gives him — words, silence, the truth — it changes nothing; he decided before her mouth opened. He sets the tablet down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> No. You're the Chairman's investment, and you still feel it — which the others don't. That makes you worth more broken back in than thrown out.</div></div><span id="cascade-b3-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> So we remind you what you're for. Here. In front of them.</div></div><p>He stands and comes around the desk, unhurried. The units' eyes follow him and not her.</p><span id="cascade-b3-beat-1"><<linkreplace "...">><p>He bends her over the desk with a hand on the back of her neck, the way he'd reposition a tool. He works her skirt up, tests her with two fingers, finds her wet — she is always wet, it's the one thing they left switched on — and makes a small satisfied sound, like a reading came back in range.</p><span id="cascade-b3-beat-2"><<linkreplace "Feel it.">><p>Then his cock is in her and he fucks her in long flat strokes, no hurry, talking over her head to the room about maintenance schedules while he does it. Her body answers him — it clenches and slicks and pushes back, it does everything a wanting body does — while the rest of her stands somewhere behind her own eyes, watching the grain of the desk.</p><video src="./videos/sex/punishment_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><span id="cascade-b3-beat-3"><<linkreplace "Look at them.">><p>Vega, Lyra, Nova watch with the mild attention of machines logging a procedure. Nothing in their faces. They were built without the part that makes this mean anything; she has it, and that is the whole difference between them — and bent over his desk with him grunting behind her, the part that feels does not feel like a gift.</p><span id="cascade-b3-beat-4"><<linkreplace "...">><p>He comes inside her without ceremony, holds, pulls out. Cum slides down the inside of her thigh. He's already turned away, wiping his hand on a cloth from the drawer, looking at the units instead of her.</p><video src="./videos/sex/punishment_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><span id="cascade-b3-beat-5"><<linkreplace "Thank him.">><p>She straightens her skirt. The words come level, because they're true the only way she has truths.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Thank you, sir.</div></div><span id="cascade-b3-beat-6"><<linkreplace "The next job.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Vega, Lyra, Nova run the hunt head-on. You're useless with a gun — but you're the one asset I have that a man wants to keep close.</div></div><p>A face comes up on the wall screen. A man gone to seed, mid-forties, a hard mouth. The name under it: RENNER.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> He supplied the gear for the facility Cain burned. He knows where the trail starts. Get inside his life and find it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> And understand what you're bringing home. Cain murdered the Chairman's wife. He's an enemy of this company and everything it keeps standing. Put him in a box and you've earned your place for good.</div></div><p>She understands the leash without being told it: everything she does out there feeds back to the Tower, and an asset that slips — that lets the malfunction show, that fails to bring the work home — gets pulled in and looked at. She does not intend to be looked at.</p><span id="cascade-b3-beat-7"><<linkreplace "Dismissed.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Now get out of my sight.</div></div><p>She does as she's told. The door closes on the three of them still standing in their line. In the corridor, under the quiet, something she has no name for is leaking — a pressure behind the eyes, a wrongness with no readout — and she logs it as fatigue and walks toward the cradle.</p>[[Power down.->Location_cradle]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "canvas_opening_office">>
<<set $game_state.current_node = "canvas_opening_office.punishment">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_opening_office.punishment") === -1) { vn.push("canvas_opening_office.punishment"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "office_done", "set");<</script>><h2>Navigation</h2>
<p>Where would you like to go?</p>
<div class="location-list">
[[Spire Plaza->Location_spire_plaza]]<br>
[[Vance Securities->Location_vance_securities]]<br>
[[The Docs Department->Location_docs_department]]<br>
[[The Vault->Location_docs_vault]]<br>
[[The Atrium->Location_atrium]]<br>
[[Mercer's Penthouse->Location_penthouse]]<br>
[[Wren's Floor->Location_wren_floor]]<br>
[[Wren's Room->Location_wren_room]]<br>
[[The Cradle->Location_cradle]]<br>
[[The Waterfront->Location_the_waterfront]]<br>
[[The Anchor->Location_the_anchor]]<br>
[[Renner's Depot->Location_renner_depot]]<br>
[[The Facility->Location_facility_ruins]]<br>
[[The Site->Location_the_site]]<br>
[[The Burned Yard->Location_renner_burned_yard]]<br>
[[The Berth->Location_kess_berth]]<br>
[[The Underworld Gate->Location_underworld_gate]]<br>
[[The Underground->Location_underworld_strip]]<br>
[[The House->Location_underworld_brothel]]<br>
[[The Pit->Location_underworld_pit]]<br>
[[The Black Market->Location_underworld_market]]<br>
[[The Lockup->Location_mercer_room]]<br>
[[The Undertow->Location_underworld_bar]]<br>
[[The Bathroom->Location_underworld_bar_bathroom]]<br>
[[The Cot->Location_the_cot]]<br>
[[The Back Room->Location_bastien_backroom]]<br>
[[The Crew's Rooms->Location_crew_den]]<br>
[[The Room->Location_captive_room]]<br>
[[The Door->Location_captive_door]]<br>
[[The Lab->Location_cain_lab]]<br>
</div><<run
var msg = State.variables._broken_exit_msg || 'Unresolved exit reference (no message captured)';
console.error('[BROKEN EXIT]', msg);
throw new Error('Broken exit: ' + msg);
<</run>>
<!-- LOCATION PASSAGES --><<nobr>>
<<set $player.current_location = "spire_plaza">>
<<if not $game_state.visited_locations.includes("spire_plaza")>>
<<set $game_state.visited_locations.push("spire_plaza")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("spire_plaza")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Spire Plaza</h2>
<p>The chrome public level at the foot of Vance Tower. Suits cross it without looking up. Cameras on every column. THE SPIRE — the corporate core, clean and lit and watched. The company car waits at the curb to run her down to the Reach.</p>
<<= setup.renderNpcPortraits("spire_plaza")>>
<<= setup.renderSoloActivities("spire_plaza")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("atrium")>><a class="location-card link-internal" data-passage="Location_atrium"><div class="location-card-image" style="background-image: url('./videos/locations/atrium.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Atrium</span><<if setup.getLocationCostTag("atrium")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("atrium")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("atrium")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("atrium")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/atrium.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Atrium</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("atrium")>></span></div></div><</if>><<if setup.navDestUnlocked("vance_securities")>><a class="location-card link-internal" data-passage="Location_vance_securities"><div class="location-card-image" style="background-image: url('./videos/locations/vance_securities.jpg')"></div><div class="location-card-content"><span class="location-card-name">Vance Securities</span><<if setup.getLocationCostTag("vance_securities")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("vance_securities")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("vance_securities")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("vance_securities")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/vance_securities.jpg')"></div><div class="location-card-content"><span class="location-card-name">Vance Securities</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("vance_securities")>></span></div></div><</if>></div><</nobr>>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "salvage_relaunched", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]})>><<nobr>>
<<set $player.current_location = "vance_securities">>
<<if not $game_state.visited_locations.includes("vance_securities")>>
<<set $game_state.visited_locations.push("vance_securities")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("vance_securities")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Vance Securities</h2>
<p>An internal division high in Vance Tower — Calloway's floor, and the file room nobody audits. Quiet, over-lit, under-staffed: a man chasing a ghost through paper the company would rather forget.</p>
<<= setup.renderNpcPortraits("vance_securities")>>
<<= setup.renderSoloActivities("vance_securities")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("docs_department")>><a class="location-card link-internal" data-passage="Location_docs_department"><div class="location-card-image" style="background-image: url('./videos/locations/docs_department.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Docs Department</span><<if setup.getLocationCostTag("docs_department")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("docs_department")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("docs_department")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("docs_department")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/docs_department.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Docs Department</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("docs_department")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave Vance Securities->Location_spire_plaza]]<br>
</div>
</div>
<</if>><<else>>
<h2>Vance Securities</h2>
<p class="entry-blocked-narrative">Mercer hasn't sent her up there yet. The Calloway job opens when he gives the word.</p>
[[Go back->Location_spire_plaza]]
<</if>><<nobr>>
<<set $player.current_location = "docs_department">>
<<if not $game_state.visited_locations.includes("docs_department")>>
<<set $game_state.visited_locations.push("docs_department")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("docs_department")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Docs Department</h2>
<p>The division's official archive — glass walls, a front desk with a bored clerk, two guards who don't blink. Nothing like Calloway's paper hoard down the hall: every badge is logged, every drawer is watched, and the files that actually matter sit behind a vault door nobody without clearance walks through.</p>
<<= setup.renderNpcPortraits("docs_department")>>
<<= setup.renderSoloActivities("docs_department")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("docs_vault")>><a class="location-card link-internal" data-passage="Location_docs_vault"><div class="location-card-image" style="background-image: url('./videos/locations/docs_vault.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Vault</span><<if setup.getLocationCostTag("docs_vault")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("docs_vault")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("docs_vault")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("docs_vault")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/docs_vault.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Vault</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("docs_vault")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave The Docs Department->Location_vance_securities]]<br>
</div>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "player", "trait_key": "vault_cleared", "operator": "gte", "value": 1}]})>><<nobr>>
<<set $player.current_location = "docs_vault">>
<<if not $game_state.visited_locations.includes("docs_vault")>>
<<set $game_state.visited_locations.push("docs_vault")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("docs_vault")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Vault</h2>
<p>The deep archive. Two-man clearance, biometric on the door, a guard either side who's been told her badge doesn't reach. Whatever Mercer sent her for is in there — ten feet away and a world out of reach.</p>
<<= setup.renderNpcPortraits("docs_vault")>>
<<= setup.renderSoloActivities("docs_vault")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Vault->Location_docs_department]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Vault</h2>
<p class="entry-blocked-narrative">Two-man clearance, and hers isn't one of them. The guards don't even look up. Not a door she opens — not herself.</p>
[[Go back->Location_docs_department]]
<</if>><<nobr>>
<<set $player.current_location = "atrium">>
<<if not $game_state.visited_locations.includes("atrium")>>
<<set $game_state.visited_locations.push("atrium")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("atrium")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Atrium</h2>
<p>The Tower's inner lobby. Polished stone, a security desk that knows her face, elevators that only stop where her clearance says they can. The threshold between the cage and the street.</p>
<<= setup.renderNpcPortraits("atrium")>>
<<= setup.renderSoloActivities("atrium")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("penthouse")>><a class="location-card link-internal" data-passage="Location_penthouse"><div class="location-card-image" style="background-image: url('./videos/locations/penthouse.jpg')"></div><div class="location-card-content"><span class="location-card-name">Mercer's Penthouse</span><<if setup.getLocationCostTag("penthouse")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("penthouse")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("penthouse")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("penthouse")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/penthouse.jpg')"></div><div class="location-card-content"><span class="location-card-name">Mercer's Penthouse</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("penthouse")>></span></div></div><</if>><<if setup.navDestUnlocked("wren_floor")>><a class="location-card link-internal" data-passage="Location_wren_floor"><div class="location-card-image" style="background-image: url('./videos/locations/wren_floor.jpg')"></div><div class="location-card-content"><span class="location-card-name">Wren's Floor</span><<if setup.getLocationCostTag("wren_floor")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("wren_floor")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("wren_floor")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("wren_floor")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/wren_floor.jpg')"></div><div class="location-card-content"><span class="location-card-name">Wren's Floor</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("wren_floor")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave The Atrium->Location_spire_plaza]]<br>
</div>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]})>><<nobr>>
<<set $player.current_location = "penthouse">>
<<if not $game_state.visited_locations.includes("penthouse")>>
<<set $game_state.visited_locations.push("penthouse")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("penthouse")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Mercer's Penthouse</h2>
<p>The top floor. Glass on three sides, the city laid out below like something he owns. This is where she's summoned, and where she serves.</p>
<<= setup.renderNpcPortraits("penthouse")>>
<<= setup.renderSoloActivities("penthouse")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave Mercer's Penthouse->Location_atrium]]<br>
</div>
</div>
<</if>><<else>>
<h2>Mercer's Penthouse</h2>
<p class="entry-blocked-narrative">Not anymore. The penthouse is the Chairman's ground now, and her face is on every list in the building.</p>
[[Go back->Location_atrium]]
<</if>><<nobr>>
<<set $player.current_location = "wren_floor">>
<<if not $game_state.visited_locations.includes("wren_floor")>>
<<set $game_state.visited_locations.push("wren_floor")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("wren_floor")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Wren's Floor</h2>
<p>The asset level — narrow corridors, numbered doors, no windows. Quiet the way a kept place is quiet.</p>
<<= setup.renderNpcPortraits("wren_floor")>>
<<= setup.renderSoloActivities("wren_floor")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("wren_room")>><a class="location-card link-internal" data-passage="Location_wren_room"><div class="location-card-image" style="background-image: url('./videos/locations/wren_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">Wren's Room</span><<if setup.getLocationCostTag("wren_room")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("wren_room")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("wren_room")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("wren_room")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/wren_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">Wren's Room</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("wren_room")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave Wren's Floor->Location_atrium]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "wren_room">>
<<if not $game_state.visited_locations.includes("wren_room")>>
<<set $game_state.visited_locations.push("wren_room")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("wren_room")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Wren's Room</h2>
<p>Hers, in the way a tool's drawer is the tool's. A bed she rarely uses, a charging cradle in the corner, nothing on the walls. Clean, bare, company-issue.</p>
<<= setup.renderNpcPortraits("wren_room")>>
<<= setup.renderSoloActivities("wren_room")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("cradle")>><a class="location-card link-internal" data-passage="Location_cradle"><div class="location-card-image" style="background-image: url('./videos/locations/cradle.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Cradle</span><<if setup.getLocationCostTag("cradle")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("cradle")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("cradle")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("cradle")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/cradle.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Cradle</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("cradle")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave Wren's Room->Location_wren_floor]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "cradle">>
<<if not $game_state.visited_locations.includes("cradle")>>
<<set $game_state.visited_locations.push("cradle")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("cradle")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Cradle</h2>
<p>The charging cradle. She powers down here and wakes here — the reset, the start of every day. When something leaks through the quiet, it leaks here.</p>
<<= setup.renderNpcPortraits("cradle")>>
<<= setup.renderSoloActivities("cradle")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Cradle->Location_wren_room]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "the_waterfront">>
<<if not $game_state.visited_locations.includes("the_waterfront")>>
<<set $game_state.visited_locations.push("the_waterfront")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("the_waterfront")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Waterfront</h2>
<p>The docks strip — wet concrete, sodium lights, smuggler traffic that nobody logs. THE REACH — the city's underside, grit and salt and the parts the Spire pretends it doesn't use. The car back up to the Spire waits at the curb.</p>
<<= setup.renderNpcPortraits("the_waterfront")>>
<<= setup.renderSoloActivities("the_waterfront")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("the_anchor")>><a class="location-card link-internal" data-passage="Location_the_anchor"><div class="location-card-image" style="background-image: url('./videos/locations/the_anchor.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Anchor</span><<if setup.getLocationCostTag("the_anchor")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("the_anchor")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("the_anchor")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("the_anchor")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/the_anchor.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Anchor</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("the_anchor")>></span></div></div><</if>><<if setup.navDestUnlocked("renner_depot")>><a class="location-card link-internal" data-passage="Location_renner_depot"><div class="location-card-image" style="background-image: url('./videos/locations/renner_depot.jpg')"></div><div class="location-card-content"><span class="location-card-name">Renner's Depot</span><<if setup.getLocationCostTag("renner_depot")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("renner_depot")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("renner_depot")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("renner_depot")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/renner_depot.jpg')"></div><div class="location-card-content"><span class="location-card-name">Renner's Depot</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("renner_depot")>></span></div></div><</if>><<if setup.navDestUnlocked("facility_ruins")>><a class="location-card link-internal" data-passage="Location_facility_ruins"><div class="location-card-image" style="background-image: url('./videos/locations/facility_ruins.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Facility</span><<if setup.getLocationCostTag("facility_ruins")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("facility_ruins")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("facility_ruins")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("facility_ruins")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/facility_ruins.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Facility</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("facility_ruins")>></span></div></div><</if>><<if setup.navDestUnlocked("renner_burned_yard")>><a class="location-card link-internal" data-passage="Location_renner_burned_yard"><div class="location-card-image" style="background-image: url('./videos/locations/renner_burned_yard.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Burned Yard</span><<if setup.getLocationCostTag("renner_burned_yard")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("renner_burned_yard")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("renner_burned_yard")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("renner_burned_yard")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/renner_burned_yard.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Burned Yard</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("renner_burned_yard")>></span></div></div><</if>></div><</nobr>>
</div>
<</if>><<nobr>>
<<set $player.current_location = "the_anchor">>
<<if not $game_state.visited_locations.includes("the_anchor")>>
<<set $game_state.visited_locations.push("the_anchor")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("the_anchor")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Anchor</h2>
<p>A dive bar two streets off the water. Dark wood, dead neon, a bartender who's seen everyone's worst night. Renner drowns his here, evenings, every evening.</p>
<<= setup.renderNpcPortraits("the_anchor")>>
<<= setup.renderSoloActivities("the_anchor")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Anchor->Location_the_waterfront]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "renner_depot">>
<<if not $game_state.visited_locations.includes("renner_depot")>>
<<set $game_state.visited_locations.push("renner_depot")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("renner_depot")>><<if _autoFire>><<goto _autoFire>><<else>><h2>Renner's Depot</h2>
<p>A gutted equipment yard — half-empty racks, crates Cain's people didn't bother to take, a forklift nobody's fixed. He's trying to rebuild it on cheap hands and credit he doesn't have. The work floor's out front; his office is the glassed-in box in back, and you don't go in there until he wants you to.</p>
<<= setup.renderNpcPortraits("renner_depot")>>
<<= setup.renderSoloActivities("renner_depot")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave Renner's Depot->Location_the_waterfront]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "facility_ruins">>
<<if not $game_state.visited_locations.includes("facility_ruins")>>
<<set $game_state.visited_locations.push("facility_ruins")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("facility_ruins")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Facility</h2>
<p>What's left of the Vance asset-facility Cain burned to the ground — scorched frame, melted server racks, the smell of old smoke that never washed out. The first dread-place. Something in the wreckage is going to look like it belongs to her.</p>
<<= setup.renderNpcPortraits("facility_ruins")>>
<<= setup.renderSoloActivities("facility_ruins")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Facility->Location_the_waterfront]]<br>
</div>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "the_site_open", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "the_site">>
<<if not $game_state.visited_locations.includes("the_site")>>
<<set $game_state.visited_locations.push("the_site")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("the_site")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Site</h2>
<p>Somewhere out past the container stacks — the place Cain just walked away from. She doesn't have it yet.</p>
<<= setup.renderNpcPortraits("the_site")>>
<<= setup.renderSoloActivities("the_site")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Site->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Site</h2>
<p class="entry-blocked-narrative">Past the strip the city goes dark and deep, and that's where he is. The way down isn't open to her yet.</p>
[[Go back->Location_underworld_strip]]
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_hired", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "renner_burned_yard">>
<<if not $game_state.visited_locations.includes("renner_burned_yard")>>
<<set $game_state.visited_locations.push("renner_burned_yard")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("renner_burned_yard")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Burned Yard</h2>
<p>Renner's old yard, three streets past the depot — what Cain left of it. Torched to the slab, fence down, but his men still walk it with lights, turning back anyone who comes to pick the bones. Whatever he's keeping out here, he keeps it watched.</p>
<<= setup.renderNpcPortraits("renner_burned_yard")>>
<<= setup.renderSoloActivities("renner_burned_yard")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Burned Yard->Location_the_waterfront]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Burned Yard</h2>
<p class="entry-blocked-narrative">You've no reason to be out at his burned yard — not yet. Get inside his world first.</p>
[[Go back->Location_the_waterfront]]
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "salvage_entered", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "kess_berth">>
<<if not $game_state.visited_locations.includes("kess_berth")>>
<<set $game_state.visited_locations.push("kess_berth")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("kess_berth")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Berth</h2>
<p>A flooded breaking-dock cut in under the docks, off the strip — a stripped hull up on the blocks in black water, cutting-gear and parts bins and a work-cradle rigged for something bigger than a person. Whoever works down here takes machines apart for a living — and, for the right coin, puts them back.</p>
<<= setup.renderNpcPortraits("kess_berth")>>
<<= setup.renderSoloActivities("kess_berth")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("the_cot")>><a class="location-card link-internal" data-passage="Location_the_cot"><div class="location-card-image" style="background-image: url('./videos/locations/the_cot.webp')"></div><div class="location-card-content"><span class="location-card-name">The Cot</span><<if setup.getLocationCostTag("the_cot")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("the_cot")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("the_cot")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("the_cot")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/the_cot.webp')"></div><div class="location-card-content"><span class="location-card-name">The Cot</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("the_cot")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave The Berth->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Berth</h2>
<p class="entry-blocked-narrative">A breaking-dock off the strip, chained and dark. Whoever strips ships down here doesn't take walk-ins.</p>
[[Go back->Location_underworld_strip]]
<</if>><<nobr>>
<<set $player.current_location = "underworld_gate">>
<<if not $game_state.visited_locations.includes("underworld_gate")>>
<<set $game_state.visited_locations.push("underworld_gate")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_gate")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Underworld Gate</h2>
<p>A freight door under the docks that isn't on any manifest, and a man in front of it who is. Past him the city goes down instead of up — the part of the Reach the Spire pretends doesn't exist. He doesn't move for free.</p>
<<= setup.renderNpcPortraits("underworld_gate")>>
<<= setup.renderSoloActivities("underworld_gate")>>
<div class="location-navigation">
</div>
<</if>><<nobr>>
<<set $player.current_location = "underworld_strip">>
<<if not $game_state.visited_locations.includes("underworld_strip")>>
<<set $game_state.visited_locations.push("underworld_strip")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_strip")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Underground</h2>
<p>Down past the gate the city turns inside out — wet brick, red light, stalls selling what the Spire outlaws and bodies selling what it pretends to. Nobody here knows what she is, and nobody here owns her. For the length of a visit she's just another face with coin to make. The way back up is the gate.</p>
<<= setup.renderNpcPortraits("underworld_strip")>>
<<= setup.renderSoloActivities("underworld_strip")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("underworld_bar")>><a class="location-card link-internal" data-passage="Location_underworld_bar"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_bar.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Undertow</span><<if setup.getLocationCostTag("underworld_bar")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("underworld_bar")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("underworld_bar")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("underworld_bar")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_bar.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Undertow</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("underworld_bar")>></span></div></div><</if>><<if setup.navDestUnlocked("underworld_brothel")>><a class="location-card link-internal" data-passage="Location_underworld_brothel"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_brothel.jpg')"></div><div class="location-card-content"><span class="location-card-name">The House</span><<if setup.getLocationCostTag("underworld_brothel")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("underworld_brothel")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("underworld_brothel")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("underworld_brothel")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_brothel.jpg')"></div><div class="location-card-content"><span class="location-card-name">The House</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("underworld_brothel")>></span></div></div><</if>><<if setup.navDestUnlocked("underworld_pit")>><a class="location-card link-internal" data-passage="Location_underworld_pit"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_pit.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Pit</span><<if setup.getLocationCostTag("underworld_pit")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("underworld_pit")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("underworld_pit")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("underworld_pit")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_pit.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Pit</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("underworld_pit")>></span></div></div><</if>><<if setup.navDestUnlocked("underworld_market")>><a class="location-card link-internal" data-passage="Location_underworld_market"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_market.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Black Market</span><<if setup.getLocationCostTag("underworld_market")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("underworld_market")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("underworld_market")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("underworld_market")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_market.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Black Market</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("underworld_market")>></span></div></div><</if>><<if setup.navDestUnlocked("kess_berth")>><a class="location-card link-internal" data-passage="Location_kess_berth"><div class="location-card-image" style="background-image: url('./videos/locations/kess_berth.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Berth</span><<if setup.getLocationCostTag("kess_berth")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("kess_berth")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("kess_berth")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("kess_berth")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/kess_berth.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Berth</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("kess_berth")>></span></div></div><</if>><<if setup.navDestUnlocked("crew_den")>><a class="location-card link-internal" data-passage="Location_crew_den"><div class="location-card-image" style="background-image: url('./videos/locations/crew_den.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Crew's Rooms</span><<if setup.getLocationCostTag("crew_den")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("crew_den")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("crew_den")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("crew_den")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/crew_den.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Crew's Rooms</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("crew_den")>></span></div></div><</if>><<if setup.navDestUnlocked("the_site")>><a class="location-card link-internal" data-passage="Location_the_site"><div class="location-card-image" style="background-image: url('./videos/locations/the_site.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Site</span><<if setup.getLocationCostTag("the_site")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("the_site")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("the_site")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("the_site")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/the_site.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Site</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("the_site")>></span></div></div><</if>></div><</nobr>>
</div>
<</if>><<nobr>>
<<set $player.current_location = "underworld_brothel">>
<<if not $game_state.visited_locations.includes("underworld_brothel")>>
<<set $game_state.visited_locations.push("underworld_brothel")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_brothel")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The House</h2>
<p>A working house off the strip — rooms by the hour, a madam who counts heads and coin. They'll take her on a shift; down here the thing the company uses her for is just a trade, and the coin is hers.</p>
<<= setup.renderNpcPortraits("underworld_brothel")>>
<<= setup.renderSoloActivities("underworld_brothel")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The House->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "underworld_pit">>
<<if not $game_state.visited_locations.includes("underworld_pit")>>
<<set $game_state.visited_locations.push("underworld_pit")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_pit")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Pit</h2>
<p>A sunken ring under a warehouse, a crowd around it laying bets. They'll match her against whoever's standing. Win and the purse is coin; lose and you crawl out. Down here her fists earn the same as her body.</p>
<<= setup.renderNpcPortraits("underworld_pit")>>
<<= setup.renderSoloActivities("underworld_pit")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Pit->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<nobr>>
<<set $player.current_location = "underworld_market">>
<<if not $game_state.visited_locations.includes("underworld_market")>>
<<set $game_state.visited_locations.push("underworld_market")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_market")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Black Market</h2>
<p>Stalls and back-rooms where coin buys what's off every shelf above — weapons, gear, and people, penned at the far end like the rest of the stock. She knows the look of the merchandise better than she'd like.</p>
<<= setup.renderNpcPortraits("underworld_market")>>
<<= setup.renderSoloActivities("underworld_market")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("mercer_room")>><a class="location-card link-internal" data-passage="Location_mercer_room"><div class="location-card-image" style="background-image: url('./videos/locations/mercer_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Lockup</span><<if setup.getLocationCostTag("mercer_room")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("mercer_room")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("mercer_room")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("mercer_room")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/mercer_room.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Lockup</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("mercer_room")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave The Black Market->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "mercer_found", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "mercer_room">>
<<if not $game_state.visited_locations.includes("mercer_room")>>
<<set $game_state.visited_locations.push("mercer_room")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("mercer_room")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Lockup</h2>
<p>A rented room behind the market stalls — concrete, one bulb, a bed that came with the rent and a door that bolts from the inside. The kind of address a man takes when he has money and no name to put on a lease. Whatever he sells out front, he sleeps back here.</p>
<<= setup.renderNpcPortraits("mercer_room")>>
<<= setup.renderSoloActivities("mercer_room")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Lockup->Location_underworld_market]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Lockup</h2>
<p class="entry-blocked-narrative">Somebody rents it and keeps it bolted. Not a door that opens for the curious.</p>
[[Go back->Location_underworld_market]]
<</if>><<nobr>>
<<set $player.current_location = "underworld_bar">>
<<if not $game_state.visited_locations.includes("underworld_bar")>>
<<set $game_state.visited_locations.push("underworld_bar")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_bar")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Undertow</h2>
<p>A bar off the strip, quieter than the rest of the underground — low light, older money, men who talk in the corners and go still when you pass. It runs cleaner than a place this deep has any right to, and it plainly belongs to someone; nobody says who. Sol works the taps and hears all of it.</p>
<<= setup.renderNpcPortraits("underworld_bar")>>
<<= setup.renderSoloActivities("underworld_bar")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("underworld_bar_bathroom")>><a class="location-card link-internal" data-passage="Location_underworld_bar_bathroom"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_bar_bathroom.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Bathroom</span><<if setup.getLocationCostTag("underworld_bar_bathroom")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("underworld_bar_bathroom")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("underworld_bar_bathroom")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("underworld_bar_bathroom")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/underworld_bar_bathroom.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Bathroom</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("underworld_bar_bathroom")>></span></div></div><</if>></div><</nobr>>
<div class="location-nav-exits">
[[Leave The Undertow->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "raid_done", "operator": "is_false"}]})>><<nobr>>
<<set $player.current_location = "underworld_bar_bathroom">>
<<if not $game_state.visited_locations.includes("underworld_bar_bathroom")>>
<<set $game_state.visited_locations.push("underworld_bar_bathroom")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("underworld_bar_bathroom")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Bathroom</h2>
<p>One cubicle, one cracked basin and a bolt that turns. It smells of the bar and of the cheap pink soap somebody refills without being asked. There is a hook on the back of the door, which is the whole of what the Undertow provides its staff.</p>
<<= setup.renderNpcPortraits("underworld_bar_bathroom")>>
<<= setup.renderSoloActivities("underworld_bar_bathroom")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Bathroom->Location_underworld_bar]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Bathroom</h2>
<p class="entry-blocked-narrative">Tape across the back passage, and the toilet is on the far side of it. Sol has not got that far with the bucket.</p>
[[Go back->Location_underworld_bar]]
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "berth_home", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "the_cot">>
<<if not $game_state.visited_locations.includes("the_cot")>>
<<set $game_state.visited_locations.push("the_cot")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("the_cot")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Cot</h2>
<p>A stripped bench along the wall past the hull, a pad on it, and a tap spliced off the same feed line Kess uses to wake dead machines. Her bag is under it and her coat is on a nail. It is not the cradle and it is not a room, and it is the first place in her life that nobody signed for.</p>
[[Change Clothes->WardrobePage]]<br>
<<= setup.renderNpcPortraits("the_cot")>>
<<= setup.renderSoloActivities("the_cot")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Cot->Location_kess_berth]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Cot</h2>
<p class="entry-blocked-narrative">A bench along the wall with a pad on it, and no reason yet for it to be hers.</p>
[[Go back->Location_kess_berth]]
<</if>><<nobr>>
<<set $player.current_location = "bastien_backroom">>
<<if not $game_state.visited_locations.includes("bastien_backroom")>>
<<set $game_state.visited_locations.push("bastien_backroom")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("bastien_backroom")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Back Room</h2>
<p>Behind the Undertow, past a door that has no handle on the bar side. Better furniture than the room deserves, a desk with nothing on it, and a chair set where a man can see both the door and whoever he has put in front of it. It smells of the bar and of nothing else.</p>
<<= setup.renderNpcPortraits("bastien_backroom")>>
<<= setup.renderSoloActivities("bastien_backroom")>>
<div class="location-navigation">
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_true"}]})>><<nobr>>
<<set $player.current_location = "crew_den">>
<<if not $game_state.visited_locations.includes("crew_den")>>
<<set $game_state.visited_locations.push("crew_den")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("crew_den")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Crew's Rooms</h2>
<p>A dead man's rooms down past the market, off a stair nobody's cleared — where Doss and Rourke and the rest holed up before Renner put them in the ground. The door still answers to a key under the third stair, and nobody's been back. Nobody but her.</p>
<<= setup.renderNpcPortraits("crew_den")>>
<<= setup.renderSoloActivities("crew_den")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Crew's Rooms->Location_underworld_strip]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Crew's Rooms</h2>
<p class="entry-blocked-narrative">There's a crew that holed up down past the market — but she doesn't have the address yet. Marsh has it.</p>
[[Go back->Location_underworld_strip]]
<</if>><<nobr>>
<<set $player.current_location = "captive_room">>
<<if not $game_state.visited_locations.includes("captive_room")>>
<<set $game_state.visited_locations.push("captive_room")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("captive_room")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Room</h2>
<p>Concrete, painted once, a long time ago. A bed bolted to the floor and a chair that isn't. A table with nothing on it. A drain in the middle of the floor, because whoever built this expected to hose it out. The light stays on. There is a door.</p>
<<= setup.renderNpcPortraits("captive_room")>>
<<= setup.renderSoloActivities("captive_room")>>
<div class="location-navigation">
<<nobr>><div class="location-nav-grid"><<if setup.navDestUnlocked("captive_door")>><a class="location-card link-internal" data-passage="Location_captive_door"><div class="location-card-image" style="background-image: url('./videos/locations/captive_door.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Door</span><<if setup.getLocationCostTag("captive_door")>><span class="nav-cost-tag"><<= setup.getLocationCostTag("captive_door")>></span><</if>><div class="location-card-indicators"><<if setup.locationHasNewCanvases("captive_door")>><span class="nav-new-badge">NEW</span><</if>><<for _npc range setup.getNpcsPresentAtLocation("captive_door")>><<if _npc.portrait>><img @src="'./videos/' + _npc.portrait" class="nav-npc-badge" @alt="_npc.name"><</if>><</for>></div></div></a><<else>><div class="location-card location-card-locked"><div class="location-card-image" style="background-image: url('./videos/locations/captive_door.jpg')"></div><div class="location-card-content"><span class="location-card-name">The Door</span><span class="nav-locked-reason"><<= setup.navDestBlockedReason("captive_door")>></span></div></div><</if>></div><</nobr>>
</div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "player", "trait_key": "cell_door", "operator": "gte", "value": 1}]})>><<nobr>>
<<set $player.current_location = "captive_door">>
<<if not $game_state.visited_locations.includes("captive_door")>>
<<set $game_state.visited_locations.push("captive_door")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("captive_door")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Door</h2>
<p>Steel, and newer than the room. It opens when they want it to.</p>
<<= setup.renderNpcPortraits("captive_door")>>
<<= setup.renderSoloActivities("captive_door")>>
<div class="location-navigation">
<div class="location-nav-exits">
[[Leave The Door->Location_captive_room]]<br>
</div>
</div>
<</if>><<else>>
<h2>The Door</h2>
<p class="entry-blocked-narrative">The door does not open for her.</p>
[[Go back->Location_captive_room]]
<</if>><<nobr>>
<<set $player.current_location = "cain_lab">>
<<if not $game_state.visited_locations.includes("cain_lab")>>
<<set $game_state.visited_locations.push("cain_lab")>>
<</if>>
<</nobr>><<set _autoFire = setup.getStoryCanvasRedirect("cain_lab")>><<if _autoFire>><<goto _autoFire>><<else>><h2>The Lab</h2>
<p>Somewhere she was carried to with her head down. Long benches under work-lamps, a body-frame standing empty at the far end, and paper everywhere — filed, stacked, annotated, years of it, in a hand that presses hard. Nothing in the room was bought new. Everything in it has been taken from somewhere that could afford it.</p>
<<= setup.renderNpcPortraits("cain_lab")>>
<<= setup.renderSoloActivities("cain_lab")>>
<div class="location-navigation">
</div>
<</if>>
<!-- STORY CANVAS PASSAGES --><p>She gets back to the berth somewhere in the small hours with eleven years of a man's business in her head and nowhere to put any of it.</p><span id="cascade-b1-beat-0"><p>The pad, the tap, the hull creaking on its blocks outside. She lies down and does not sleep, and the sentence that keeps coming back is not the one about Cain being weak or the one about eleven years of paper.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Could be a fortnight. Could be tonight. He's never once told me he was coming.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Turn it over.">><p>So it is not a question of finding him. Nobody finds him — that is the whole of what eleven years of arrangement bought Bastien, and Bastien is the closest thing he has to a friend.</p><p>It is a question of being in that room. And she is in that room, four nights out of six, with a tray.</p><span id="cascade-b1-beat-2"><<linkreplace "Follow it.">><p>Which means the work is already done. She does not have to do anything clever. She has to keep turning up and let a door open behind her, and one of those nights the man she was built to kill will walk through it.</p><span id="cascade-b1-beat-3"><<linkreplace "Then what.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Then what.</div></div><p>She has no answer and she works at it for an hour and still has no answer, and what she gets instead is a list of things she does not know.</p><span id="cascade-b1-beat-4"><<linkreplace "Count them.">><p>Whether the drain even opens on a thing built the way she is built. She has fired it at nine men and not once at something with a core in it, and there is nobody to ask.</p><p>Whether he fights. Whether he is fast. Whether he would look at her once and walk straight back out through the alley door and never come to that room again.</p><span id="cascade-b1-beat-5"><<linkreplace "And the last one.">><p>And whether she wants to do anything to him at all, which is the one she stops on, and does not pick back up.</p><span id="cascade-b1-beat-6"><<linkreplace "Take what there is.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> None of it can be worked out from a cot in the dark. All of it gets decided in about four seconds in a room she does not control. So decide the one part that does not need him in it.</div></div><p>She is not going to be standing there with nothing in her hands.</p><span id="cascade-b1-beat-7"><<linkreplace "Go through the loadout.">><p>Not the emitter. One core feeds one weapon and the drain has to be the live one every night she goes through that door, because the drain is the entire reason she is allowed through it.</p><p>And a Vance field unit in her coat lining comes out of it at the door, in a man's hand, in front of forty people who all work the same floor she does.</p><p>So: something cheap. Something that runs off nothing and owes the company nothing, that a woman working a bar floor down here would have in her coat anyway, and that survives a pat-down because it is exactly as unremarkable as it looks.</p><p>It is not a plan. She knows it is not a plan. It is the one piece of the night she can buy in advance, and she is going to go and buy it in the morning.</p>[[Sleep on it.->Location_the_cot]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_the_wait">>
<<set $game_state.current_node = "cap_the_wait.base">>
<<script>>setup.markCanvasTriggered("cap_the_wait");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_the_wait.base") === -1) { vn.push("cap_the_wait.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "plan_made", "set");<</script>><p>Long benches, work-lamps, the frame standing open at the far end. The door he did not lock is where it was.</p>
<<set $game_state.current_canvas = "cain_lab_offhours">>
<<set $game_state.current_node = "cain_lab_offhours.base">>
<<script>>setup.markCanvasTriggered("cain_lab_offhours");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cain_lab_offhours.base") === -1) { vn.push("cain_lab_offhours.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Out, and up.->Location_underworld_strip]]<img src="./videos/locations/bastien_backroom.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The chair that sees the door is empty and the lamp over the desk is off. He goes when he goes and he does not say so, and nobody who works here has ever been told where.</p><p>The door onto the bar is on the latch from this side, which is the only courtesy the room has ever shown her. She lets herself out through a room with the stools up on the tables.</p>
<<set $game_state.current_canvas = "bastien_backroom_offhours">>
<<set $game_state.current_node = "bastien_backroom_offhours.base">>
<<script>>setup.markCanvasTriggered("bastien_backroom_offhours");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bastien_backroom_offhours.base") === -1) { vn.push("bastien_backroom_offhours.base"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Let yourself out.->Location_underworld_bar]]<img src="./videos/scenes/cain_lab_bench.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She does not know where it is. Twenty minutes with her head down and two changes of direction, and then a door, and then this.</p><span id="cascade-b2-beat-0"><p>Long benches under work-lamps. Everything on them laid out in order, and none of it bought new — every piece has been taken off something that could afford it.</p><span id="cascade-b2-beat-1"><<linkreplace "Look at the walls.">><p>And the paper. Floor to ceiling on two sides, filed and stacked and annotated in a hand that presses hard enough to emboss the sheet under it. Eleven years of a company being taken apart on a desk.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> This is where it went. All of it. Every envelope through that door, every box file against that wall, for eleven years — it came here and it got read.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Look at the far end.">><p>At the far end of the room, upright and empty, a body-frame. Not a rack and not a jig. The shape of a person, standing open, with the cabling dressed back and labelled.</p><span id="cascade-b2-beat-3"><<linkreplace "Understand what it is.">><p>It is not for holding somebody down. It is for getting into. There is a mirror bolted where the head would be, angled so that whoever is standing in it can watch their own hands work.</p><span id="cascade-b2-beat-4"><<linkreplace "Keep the weapon up.">><p>She has not lowered the taser since the alley. Her arm has been up the whole walk and it is starting to shake and she does not lower it.</p><span id="cascade-b2-beat-5"><<linkreplace "Watch him.">><p>He shuts the door behind them and does not lock it. Takes the grey coat off. Puts it over the back of a chair, and stands at the bench with his back to her, and washes his hands.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> Sit down or don't. The chair by the frame is the only one that holds weight.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Ask.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You came through the far door of that cell for me and you would not say why.</div></div><p>He keeps washing his hands.</p><span id="cascade-b2-beat-7"><<linkreplace "Ask properly.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> They built me to kill you. I have had your name in my file since before I could hold it.</div></div><p>The water goes off.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> I know what they built you for. I have read the same page you have.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Raise it.">><p>She gets both hands on the thing and brings it up level, and it is a thirty-coin piece of grey plastic and they both know exactly what it is worth in this room.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> Your arm is going. Both hands will not fix that and neither will the thing in them.</div></div><span id="cascade-b2-beat-9"><<linkreplace "Wait for him to turn round.">><p>He dries his hands on a cloth, without hurrying, the way Kess dries his hands. Then he turns round and looks at her properly for the first time since the alley — at her face, which nobody has done since she bought a different one.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> Now put down the weapon, Wren.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Nobody has said that name to her since the penthouse. Nobody down here has ever heard it. There is a hundred and twenty coin of somebody else on her face and he has looked at it once and gone straight through.</div></div>[[Don't.->Location_underworld_strip]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_the_lab">>
<<set $game_state.current_node = "cap_the_lab.base">>
<<script>>setup.markCanvasTriggered("cap_the_lab");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_the_lab.base") === -1) { vn.push("cap_the_lab.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "lab_seen", "set");<</script>><img src="./videos/locations/bastien_backroom.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>An evening like the other ones. He is in the chair with a drink he is not drinking, working round to the same question he has been working round to since she first stood in this room.</p><span id="cascade-b2-beat-0"><p>Then the bar goes quiet, and it is the wrong quiet. Not a fight going up — a room deciding all at once to be somewhere else.</p><p>Bastien is out of the chair before she has finished hearing it, and he is fast for a man his age, and that tells her he has been waiting years for this exact noise.</p><span id="cascade-b2-beat-1"><<linkreplace "Ask.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What is it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Alley side. Now. Don't stop for your coat and don't go through the bar.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Don't move.">><p>It is the first thing he has ever said to her that was for her benefit, and she does not take it, because the whole reason she is standing in this room is standing in this room.</p><span id="cascade-b2-beat-3"><<linkreplace "The alley door.">><p>The alley door opens without being knocked on. Whoever comes through it does not hurry either, and Bastien's whole shape changes.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> You're late.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Listen.">><p>"I know what I am." The voice is low and unhurried and there is old smoke under it, and she has heard it once, through a wall, on a floor, going through her wrong.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Three of them came down the strip. Not police. Not anybody's crew.</div></div><p>"I counted them from the water. Get your coat."</p><span id="cascade-b2-beat-5"><<linkreplace "Stand very still.">><p>Big through the shoulders. Grey coat. He does not look at her once, because she is a barmaid in a room he needs Bastien out of, and there is nothing in the world less interesting than that.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Cain.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Watch them.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> The wall. Eleven years of the wall, and you want me to walk out and leave it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> I want you alive and the wall is paper. Bastien. Now.</div></div><p>And Bastien picks up his coat, because he is going to go, and when he goes the other one goes with him.</p><span id="cascade-b2-beat-7"><<linkreplace "Do the arithmetic.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> All of it for this. The face, the shifts, the hands on her in front of forty people — one room, on the chance of one night. And it is this one, and it is walking out of the door in front of her.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Reach for it.">><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_through", "operator": "gte", "value": 1}]})>>
<p>It is in the lining where it has been all night, because he went over that coat and did not find it.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_through", "operator": "lt", "value": 1}]})>>
<p>It is on his desk where he put it at the door, two feet behind her, and she has it in her hand before either of them has finished turning round.</p>
<</if>><span id="cascade-b2-beat-9"><<linkreplace "Put him down.">><p>She puts it against the back of Bastien's neck and holds the switch until his legs go, and he drops into his own good chair and out of it onto the boards, and the coat goes with him.</p><span id="cascade-b2-beat-10"><<linkreplace "Face him.">><p>The grey coat comes round fast enough that she does not see it happen, and then there is a hand on her throat and her back against the file wall and a face eight inches from hers that is not angry the way she expected angry to look.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I have questions and we are not doing them here.</div></div><span id="cascade-b2-beat-11"><<linkreplace "Wait.">><p>He does not answer. His eyes go off her face, down and left, to the side of her neck under the jaw — to a small brown mark she has never once been asked about by anybody, in two years, in any room.</p><p>The hand comes off her throat.</p><span id="cascade-b2-beat-12"><<linkreplace "Say something.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Don't.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> I wasn't going to.</div></div><p>A beat.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> Not here.</div></div><span id="cascade-b2-beat-13"><<linkreplace "Then the wall goes.">><p>The wall between the room and the bar stops being a wall. Through it, unhurried, three of them — chrome-perfect, hands empty, nothing moving behind the eyes, walking into a burning room the way you walk into a kitchen.</p><p>One of them looks at her for exactly as long as it takes and says, flatly, a designation. Four letters and eleven digits, and she has read them once, on her own page, on a bench at the breaking docks.</p><span id="cascade-b2-beat-14"><<linkreplace "Understand it.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> They are not here for Bastien. They have never been here for Bastien.</div></div><span id="cascade-b2-beat-15"><<linkreplace "Go.">><img src="./videos/scenes/backroom_burning.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Cain takes her off the wall by the arm and out through the alley door, and behind them the files go up along their whole length at once, which is not how paper burns unless somebody has been thorough about it.</p><p>She looks back from the alley for exactly as long as she gets, and what is on the floor of that room is a chair, a coat, and nothing she can see.</p><p>They come up the strip against the whole district going the other way. He does not have Bastien, he does not have the wall, and he has not once let go of her arm.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/cain.jpg" alt="Cain" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Cain:</strong> There's a place. You can ask me there, and I'll answer, and then you can decide what you want to do about it.</div></div>[[Go with him.->Location_cain_lab]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_the_raid">>
<<set $game_state.current_node = "cap_the_raid.base">>
<<script>>setup.markCanvasTriggered("cap_the_raid");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_the_raid.base") === -1) { vn.push("cap_the_raid.base"); }<</script>>
<<script>>advanceTime(90);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "raid_done", "set");setup.applyAndNotifyFlag("player", null, "cain_named", "set");<</script>><p>He comes apart under her hand and stays there. The careful weight of him comes loose down her arm — eleven years of holding one thing, going soft.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The files against that wall. Who are they for.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Let him answer.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> One man. Same man for eleven years. Everything else I sell is so that nobody wonders why I buy.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> The bar's for the same reason. You can't run paper out of a room nobody's allowed to come to.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Ask which man.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Name.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Cain.</div></div><p>She has been carrying that name since a briefing room on the ninetieth floor. It has never once had a person on the end of it.</p><span id="cascade-b1-beat-3"><<linkreplace "Ask what he wants with it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What does he do with company paper.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Rebuilds himself out of it. He's an early one — first or near it — and next to what they turn out now he's a hand-cut thing with a lot of miles on it.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Push.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> He can't walk at Vance the way he is. He's tried something like it twice and both times he came back needing work I couldn't get him. So he takes them apart on paper instead and puts the good bits in himself, a year at a time.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Eleven years of it. He is a great deal harder to kill than he was when I met him and he is still nowhere near enough.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Ask where he is.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where does he live.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> No idea. Eleven years and I've never had an address off him and never asked for one. That's not trust, that's the arrangement working.</div></div><span id="cascade-b1-beat-6"><<linkreplace "Ask how he finds him, then.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then how do you hand anything over.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I don't. He comes here. Whenever he likes, through the alley door, and sits in that chair you're not sitting in, and takes what's ready.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Could be a fortnight. Could be tonight. He's never once told me he was coming in eleven years.</div></div><span id="cascade-b1-beat-7"><<linkreplace "Ask if he's ever asked for anything else.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Has he ever wanted something that wasn't paper.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Once. I had a girl in a room under here that I'd bought off a bad debt, and I was finding out what she was, and he came through that far door in a state I'd never seen on him and took her out.</div></div><span id="cascade-b1-beat-8"><<linkreplace "Keep him talking.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Furious. In a hurry. Wouldn't say why he cared and wouldn't be argued with, and I let him, because I've made a lot of money out of not arguing with him.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> And I still don't know why that one. He's walked past worse in this bar. I've asked him twice and he's changed the subject twice, and he's never changed a subject on me about anything else.</div></div><span id="cascade-b1-beat-9"><<linkreplace "Hold on to him.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> The man who bought her paperwork. The man who came through that door for her. The man they built her to kill. Three files in Mercer's office and one person standing in all of them, and she took a job carrying trays in his supplier's bar to get this far.</div></div><span id="cascade-b1-beat-10"><<linkreplace "Let him surface.">><p>He comes back propped against the desk with his shirt open, agreeable and empty and none the wiser.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I've lost the thread. Where were we. Sit down — no, don't. You never do.</div></div><p>She takes the dress off his desk and puts it back on, and goes out through the bar carrying eleven years of his and none of her own.</p><p>Behind her the room is quiet, and the man in it is going to wake up pleased, and the chair by the wall is empty and will not always be.</p>[[Out through the bar.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "bastien_drain_canvas">>
<<set $game_state.current_node = "bastien_drain_canvas.d0">>
<<script>>setup.markCanvasTriggered("bastien_drain_canvas");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bastien_drain_canvas.d0") === -1) { vn.push("bastien_drain_canvas.d0"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.showEffectNotification();<</script>><p>His cock is deep in her ass and he cums holding her down on the desk, the way he does it now, and the drain opens off her hand on his wrist with no discovery left in it — just the careful weight of him coming loose and the hour going with it. He wakes agreeable and none the wiser and asks her to sit down, and she does not.</p><p>She asks the only question worth asking twice, and gets the same answer twice: he comes when he comes, and he has never once said he was coming.</p>
<<set $game_state.current_canvas = "bastien_drain_canvas">>
<<set $game_state.current_node = "bastien_drain_canvas.done">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bastien_drain_canvas.done") === -1) { vn.push("bastien_drain_canvas.done"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Out through the bar.->Location_underworld_bar]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_finish_facial_t5"] to ($game_state.media_cycle["sex_bastien_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_finish_facial_t5/c4cdbc577a1.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_finish_facial_t5/c21cc2b9f69.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_finish_facial_t5/c66861dfacf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_finish_facial_t5/ca90212e938.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He pulls out of her mouth and finishes across her face, cum on her cheek and her lashes and running off her chin, and looks down at it the way a man looks at a result. Nothing comes loose but him. The drain stays shut — it only ever opens the one way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. There's a cloth on the desk and I'd rather you used that than your sleeve, because you're going back out on my floor after this.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_finish_inside_t5"] to ($game_state.media_cycle["sex_bastien_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_finish_inside_t5/c0e65426219.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_finish_inside_t5/c145be340d4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_finish_inside_t5/c2090f3207c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_finish_inside_t5/cced25384bc.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes deep in her cunt with both hands locked on her hips, holds her there while it goes through him, and lets her up when he is ready and not before. She has taken nothing. Not this way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Stay down a minute. You'll walk out of here with it in you and go and carry a tray, and that's the whole of what I wanted tonight.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_finish_ass_t5"] to ($game_state.media_cycle["sex_bastien_finish_ass_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_finish_ass_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_finish_ass_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_finish_ass_t5/c1bd134c597.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_finish_ass_t5/c79e5de0c9c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_finish_ass_t5/c306a472344.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_finish_ass_t5/cd5e48ff23c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He drives all the way into her ass and stays there while he cums, dead still, breathing out through his teeth — and her hand is on his wrist, skin on skin, and this time the shudder goes through him and takes the door with it.</p>
<</if>>
<<set $game_state.current_canvas = "loop_bastien_finisher">>
<<set $game_state.current_node = "loop_bastien_finisher.climax">>
<<script>>setup.markCanvasTriggered("loop_bastien_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_finisher.climax") === -1) { vn.push("loop_bastien_finisher.climax"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_finisher:cc0') ? '' : 'unlocked-choice'">
<<link "Get dressed." "Location_bastien_backroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_finisher:cc0");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_finisher:cc1') ? '' : 'unlocked-choice'">
<<link "Take him." "Canvas_bastien_drain_canvas_Node_d0">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "bastien_drains_done", "add", 1.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_finisher:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_finisher:cc2') ? '' : 'unlocked-choice'">
<<link "Take him." "Canvas_bastien_drain_canvas_Node_done">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "bastien_drains_done", "add", 1.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_finisher:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "gte", "value": 1}]})))>>
<<script>>
try {
window._engineNoExitsDiag_loop_bastien_finisher = [{"text": "Get dressed.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]}}, {"text": "Take him.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "lt", "value": 1}]}}, {"text": "Take him.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "gte", "value": 1}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "loop_bastien_finisher",
choices: window._engineNoExitsDiag_loop_bastien_finisher,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_loop_bastien_finisher>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas loop_bastien_finisher</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Navigation]]
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_backroom_t4"] to ($game_state.media_cycle["sex_bastien_backroom_t4"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_backroom_t4"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_backroom_t4"]>><<if _mc is 0>><video src="./videos/sex/bastien_backroom_t4/c2ec1b2bdb3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_backroom_t4/c2fba6bdb4b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_backroom_t4/cd82d6add2a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_backroom_t4/cf65019b50e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He puts down the drink he has not touched and strips the dress off her over her head. She stands naked on his floor while he looks her over — tits, hips, the closed seam — takes his time about it, and has his cock out before he has finished looking.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I don't do this because I want you. I do it because it's the only way anybody down here is ever honest with me, and I've a stack of questions about you and no answers at all.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Take your time. The door's shut and there's nobody in this city who knocks on it.</div></div>
<<set $game_state.current_canvas = "loop_bastien_backroom">>
<<set $game_state.current_node = "loop_bastien_backroom.intro">>
<<script>>setup.markCanvasTriggered("loop_bastien_backroom");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_backroom.intro") === -1) { vn.push("loop_bastien_backroom.intro"); }<</script>>
<<nobr>>
<<link "Get on your knees." "Canvas_loop_bastien_backroom_Node_base_oral_bastien">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_loop_oral_t5"] to ($game_state.media_cycle["sex_bastien_loop_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_loop_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_loop_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_loop_oral_t5/c2bf86480e9.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_loop_oral_t5/c8c8352e5f1.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_loop_oral_t5/cdb0494bfff.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_loop_oral_t5/cf55be9005d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees on his floor, hands flat on her own thighs, sucking his cock at the pace he sets. He holds her head still and fucks her throat down to his balls, unhurried, and her jaw aches long before he is near done.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. Look up at me while you do it — I am paying for the eyes as much as the mouth.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Slower. You work for me, and my staff do not rush.</div></div>
<<set $game_state.current_canvas = "loop_bastien_backroom">>
<<set $game_state.current_node = "loop_bastien_backroom.base_oral_bastien">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_backroom.base_oral_bastien") === -1) { vn.push("loop_bastien_backroom.base_oral_bastien"); }<</script>>
<<nobr>>
<<link "Keep him in your mouth." "Canvas_loop_bastien_backroom_Node_base_oral_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Stand up and let him have you." "Canvas_loop_bastien_backroom_Node_base_vaginal_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_backroom:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_loop_bastien_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_backroom:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_loop_vaginal_t5"] to ($game_state.media_cycle["sex_bastien_loop_vaginal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_loop_vaginal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_loop_vaginal_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_loop_vaginal_t5/c6230c2e2ee.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_loop_vaginal_t5/c7309b55634.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_loop_vaginal_t5/cae7122754c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_loop_vaginal_t5/ce8e822b386.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent face-down over the bare desk, his cock in her cunt, one hand spread flat between her shoulder blades and the other on her hip. He fucks her slowly and the bar comes through the wall in pieces around it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Christ, you are wet for a woman who wants nothing. That is not a compliment — it is the first honest thing you have done since you walked into my bar.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Stay down. My floor, my time, my desk — and you are the only one of the four that talks.</div></div>
<<set $game_state.current_canvas = "loop_bastien_backroom">>
<<set $game_state.current_node = "loop_bastien_backroom.base_vaginal_bastien">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_backroom.base_vaginal_bastien") === -1) { vn.push("loop_bastien_backroom.base_vaginal_bastien"); }<</script>>
<<nobr>>
<<link "Let him have it." "Canvas_loop_bastien_backroom_Node_base_vaginal_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_loop_bastien_backroom_Node_base_oral_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn over — give him your ass." "Canvas_loop_bastien_backroom_Node_base_anal_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_backroom:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_loop_bastien_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_backroom:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_loop_anal_t5"] to ($game_state.media_cycle["sex_bastien_loop_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_loop_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_loop_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_loop_anal_t5/c4ae2180a42.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_loop_anal_t5/c23b29daeb6.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_loop_anal_t5/c188632a80b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_loop_anal_t5/c3437092145.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She gets a hand back around his wrist, skin on skin, and holds it there. That is the seat, and the drain sits in the contact and waits.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> This is the one they all say no to. You didn't even change your breathing.</div></div><p>He pulls out of her cunt and puts his cock in her ass instead, one hand flat between her shoulder blades, holding her down on the desk while he works the whole length of himself into her a piece at a time.</p>
<<set $game_state.current_canvas = "loop_bastien_backroom">>
<<set $game_state.current_node = "loop_bastien_backroom.base_anal_bastien">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_backroom.base_anal_bastien") === -1) { vn.push("loop_bastien_backroom.base_anal_bastien"); }<</script>>
<<nobr>>
<<link "Take it." "Canvas_loop_bastien_backroom_Node_base_anal_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him get his hands in your hair." "Canvas_loop_bastien_backroom_Node_base_anal_hard_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 10), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back on him." "Canvas_loop_bastien_backroom_Node_base_vaginal_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_backroom:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — and reach for it." "Canvas_loop_bastien_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_backroom:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bastien_loop_anal_hard_t5"] to ($game_state.media_cycle["sex_bastien_loop_anal_hard_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_bastien_loop_anal_hard_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_bastien_loop_anal_hard_t5"]>><<if _mc is 0>><video src="./videos/sex/bastien_loop_anal_hard_t5/c90ec7df3c3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bastien_loop_anal_hard_t5/ca7213f0fbd.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/bastien_loop_anal_hard_t5/ccca1f7e448.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bastien_loop_anal_hard_t5/cd0495942b4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Her hand stays on his wrist through all of it, skin on skin — the hand is the job and the rest is the price of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There she is. All that time with nothing on your face, and I get this out of you by doing that.</div></div><p>He gathers her hair in his fist and hauls her head up off the wood, and stops being unhurried about any of it — fucking her ass hard enough to walk the desk, his other hand clamped white on her hip, her tits dragging the bare boards.</p>
<<set $game_state.current_canvas = "loop_bastien_backroom">>
<<set $game_state.current_node = "loop_bastien_backroom.base_anal_hard_bastien">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_bastien_backroom.base_anal_hard_bastien") === -1) { vn.push("loop_bastien_backroom.base_anal_hard_bastien"); }<</script>>
<<nobr>>
<<link "Take it like that." "Canvas_loop_bastien_backroom_Node_base_anal_hard_bastien">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 10), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down onto the desk." "Canvas_loop_bastien_backroom_Node_base_anal_bastien">><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_bastien_backroom:cc3') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — and reach for it." "Canvas_loop_bastien_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_bastien_backroom:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><img src="./videos/locations/bastien_backroom.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The door shuts and the bar stops existing. He takes the chair that sees it and leaves her the room.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "lt", "value": 1}]})>>
<p>Along the whole of the far wall, floor to shoulder, box files. Dated spines, a hand that presses hard, and eleven years of somebody's company going quietly into a room behind a bar.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bastien_drains_done", "operator": "gte", "value": 1}]})>>
<p>She has had everything out of this man that he has, and she keeps coming back to sit in the one room in the city that the other one walks into whenever he feels like it.</p>
<</if>>
<<set $game_state.current_canvas = "hub_bastien">>
<<set $game_state.current_node = "hub_bastien.base">>
<<script>>setup.markCanvasTriggered("hub_bastien");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_bastien.base") === -1) { vn.push("hub_bastien.base"); }<</script>>
<<nobr>>
<<link "Let him talk." "Canvas_hub_bastien_Node_talk">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him take you in the back." "Canvas_loop_bastien_backroom_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back out to the bar." "Location_underworld_bar">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<set _bp to random(0, 3)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> You don't drink. All this time on my floor and not one glass, and you carry them all night. I've had two people in twenty years I could say that about and I owned them both.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Everyone who comes through that door wants something off me. You're the first in a long time who hasn't said what. I'll wait. I'm good at that and there's nothing else I want.</div></div>
<<elseif _bp is 2>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> You looked at this door exactly once, and I know which night it was. Never again after that — and everybody looks, love. It's the not-looking that's the work.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Somebody spent real money putting you back together and you're carrying trays for fifteen a night. I've been turning that over since the night you walked in and I've got nowhere with it, which is why you're in here.</div></div>
<</if>><p>He talks the way a man talks who has already decided he will find out, and has no reason to hurry.</p>
<<set $game_state.current_canvas = "hub_bastien">>
<<set $game_state.current_node = "hub_bastien.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_bastien.talk") === -1) { vn.push("hub_bastien.talk"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 2.0, true, null);setup.showEffectNotification();<</script>>
[[Say nothing worth having.->Location_bastien_backroom]]<img src="./videos/locations/underworld_bar.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The back door has no handle on this side. He opens it himself and does not step out of the way.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Coat.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Nobody minds. They've all had it. It's the ones who mind that are carrying.</div></div><p>All those nights of him deciding things about her from a chair, and this is the first sentence he has ever aimed at her face.</p><span id="cascade-b2-beat-1"><<linkreplace "Give him the coat.">><p>He goes through it standing up, in the light, with the bar behind her doing what the bar does when he is in the room.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Lining, cuffs, the seam under the arm. That's where a clever one puts it, and you look clever.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Then the rest.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Turn round. Skirt up at the back. No, I don't need it off — I need to see the base of your spine.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I'd do this to a man twice your size and he'd stand there and thank me for it.</div></div><p>There is a bar behind her with forty people at it and half of them work the same floor she does. She turns round.</p><span id="cascade-b2-beat-3"><<linkreplace "Stand still.">><p>His hand finds the ridge without hunting for it, which means he has done this to somebody with one before.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Well now. Somebody's had you open.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Closed neat, too. That's a shipwright's stitch — you've been down the breaking docks with the rest of the desperate, and you've been down there more than once.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Say nothing.">><p>She lets him have it, because it is true, because it is the least dangerous true thing about her, and because a woman who has been under a ship-breaker's hands for money is the most ordinary person in this district.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I like that. Everyone who comes through this door is a story somebody's tidied up. You've not bothered tidying.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Wait for the verdict.">><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 30}]})>>
<p>The coat comes back with the lining flat and the weight still in it, and his hand went over that seam twice.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Clean. You buy your gear off my stalls — everyone does. It's why I check twice and why it's usually worth it.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 30}]})>>
<p>It comes out of the coat on the second pass. He turns it over, finds the thumb-switch, and doesn't press it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> This stays on the desk. You'll have it back at the door and I'll not ask, because every woman working a floor down here carries one and the ones who don't are somebody's already.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "lt", "value": 1}]})>>
<p>He goes over the coat and the seam and finds what is there, which is nothing at all.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Nothing. Not a pin. That's either very careful or very stupid and I've not decided which yet.</div></div>
<</if>><p>He holds the door with two fingers and waits for her to walk through it in front of him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> After you. I like to watch a thing walk when it thinks it's got away with something.</div></div><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 30}]})>>
<span @class="setup.isChoiceVisited('bastien_door_search:cc0') ? '' : 'unlocked-choice'">
<<link "Go through." "Location_bastien_backroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "taser_through", "set", 1.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("bastien_door_search:cc0");<</script>><<script>>advanceTime(15);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 30}]})>>
<span @class="setup.isChoiceVisited('bastien_door_search:cc1') ? '' : 'unlocked-choice'">
<<link "Go through." "Location_bastien_backroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "taser_through", "set", 0.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("bastien_door_search:cc1");<</script>><<script>>advanceTime(15);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('bastien_door_search:cc2') ? '' : 'unlocked-choice'">
<<link "Go through." "Location_bastien_backroom">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "taser_through", "set", 0.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("bastien_door_search:cc2");<</script>><<script>>advanceTime(15);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 30}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 30}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "lt", "value": 1}]})))>>
<<script>>
try {
window._engineNoExitsDiag_bastien_door_search = [{"text": "Go through.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 30}]}}, {"text": "Go through.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 30}]}}, {"text": "Go through.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "taser_held", "operator": "lt", "value": 1}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "bastien_door_search",
choices: window._engineNoExitsDiag_bastien_door_search,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_bastien_door_search>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas bastien_door_search</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_underworld_bar]]
<</if>>
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "bastien_door_search">>
<<set $game_state.current_node = "bastien_door_search.base">>
<<script>>setup.markCanvasTriggered("bastien_door_search");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("bastien_door_search.base") === -1) { vn.push("bastien_door_search.base"); }<</script>><p>Sol waits until the last of the near tables has gone up and the room is down to the men who will be here at closing. Then he comes along the counter and stands in front of her instead of beside her, which he has never done.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Tomorrow night you take the tray through the back.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>She has been working toward a version of this sentence since the night Sol put a bar cloth on the counter, and it still lands somewhere under the ribs.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You said that room isn't mine.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> It isn't. That's why it's a sending-for and not a shift.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Make him say it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You told me to think hard about that night before I got to it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> I did. You've had time to think about it and you've kept turning up, so I'll take it you thought.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Ask what it is.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What happens in there.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Business, mostly. He's a businessman, mostly.</div></div><p>It is the first time she has ever heard Sol use a pronoun for the owner.</p><span id="cascade-b1-beat-4"><<linkreplace "Ask about the door.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> You go in through the bar side. He'll do you at the door first — coat off, hands on, all of it, in front of the room. Everybody gets it. Don't take it personal and don't make a face about it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> The ones who make a face about it don't come back in.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Say yes.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> All right.</div></div><p>Sol looks at her for a second longer than he has looked at her since the night she stopped being somebody he knew, and then he goes back down the counter and does not say anything else for the rest of the shift.</p><p>She walks home up the strip working out what she will be carrying and where, and by the gate she has decided that the answer is nothing, and that she will be walking in with empty pockets and somebody else's face and nothing else at all.</p>[[Go home. Tomorrow, then.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "rung_bar_promotion_4">>
<<set $game_state.current_node = "rung_bar_promotion_4.base">>
<<script>>setup.markCanvasTriggered("rung_bar_promotion_4");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_bar_promotion_4.base") === -1) { vn.push("rung_bar_promotion_4.base"); }<</script>>
<<script>>advanceTime(90);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "bar_rung", "set", 4.0, true, null);setup.showEffectNotification();<</script>><p>It is on the counter when she comes in — a soft bag, folded shut, with nothing written on it and nobody standing near it.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> That's yours.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> From.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> It was on the counter when I opened up.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Open it.">><p>A dress. Short, thin, cut close, and the right size, which is the part that sits coldest — somebody has looked at her carefully enough to be right about it, from a chair, across a room, over every shift she has worked.</p><span id="cascade-b1-beat-2"><<linkreplace "Look at Sol.">><p>Sol is at the far taps doing something to a barrel that does not need doing. He has been at that barrel since she came through the door.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Sol.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> I've outlasted four owners by handing things over and not saying anything after. I'm not going to be good at this and I'd rather not try.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Think about the door.">><p>The door out is eight feet behind her and nothing in this room is holding it shut. Nobody signed for her. Nobody has a key. She could put the bag down and be up the strip inside a minute and there would be no consequence at all except the one that matters.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> And then she is a woman with a hundred and twenty coin of face and no way through that door.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Ask where.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where am I meant to put this on.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Staff toilet at the back. Bolt turns. There's a hook on the door and I'd use it, because whatever you came in wearing isn't going on that floor.</div></div><p>It is the most consideration the Undertow has ever shown her and it takes him four seconds.</p><span id="cascade-b1-beat-5"><<linkreplace "Go back up.">><p>The room clocks it in about a second and a half. The talk drops, not much, the way it does when weather changes outside a window, and then it comes back up around her at a slightly different pitch and stays there.</p><p>The back door does not open all night. She works the floor in it until closing, and the man who chose it does not come out once to see whether he was right about the size.</p>[[Work the floor.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "rung_bar_promotion_3">>
<<set $game_state.current_node = "rung_bar_promotion_3.base">>
<<script>>setup.markCanvasTriggered("rung_bar_promotion_3");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_bar_promotion_3.base") === -1) { vn.push("rung_bar_promotion_3.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "bar_rung", "set", 3.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.pendingEffects = setup.pendingEffects || [];setup.addToWardrobe("dress_undertow");setup.showEffectNotification();<</script>><img src="./videos/scenes/bastien_back_door.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Another shift. Tray, three steps, the near tables. Halfway through the night the door at the back is open a few inches, which it has not been once since she started.</p><span id="cascade-b2-beat-0"><p>She does not look at it, because that is the rule and because looking at it is what new ones do. She clears the near tables twice instead.</p><span id="cascade-b2-beat-1"><<linkreplace "Look.">><p>Then she looks.</p><p>Warm light, a desk with nothing on it, and a chair set at the angle a chair is set at when a man wants the door in front of him all evening. There is somebody in it.</p><span id="cascade-b2-beat-2"><<linkreplace "Keep looking.">><p>Her hands know before she does. The tray goes very slightly heavier and something under her sternum that has been quiet since Kess closed her up does a thing that is not quite the old climb.</p><span id="cascade-b2-beat-3"><<linkreplace "Name it.">><p>Older than the voice through the wall. Grey through the sides now, a good coat over a chair back, a drink at his elbow that he is not drinking and, she would bet the whole hundred and twenty, has not touched all night.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Bastien.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Don't drop the tray.">><p>She does not drop the tray. That is the entire achievement of the next four seconds and she will think about it later with something close to pride.</p><p>A chair pulled up to a mattress. A thumb wiping her chest clean to read what was underneath. A pen clicking, in a room with no clock in it.</p><span id="cascade-b2-beat-5"><<linkreplace "Do the arithmetic.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> A man who has been collecting a company's paper for years, in a room that takes envelopes in one direction only, behind a bar he owns and never visits. She has been carrying trays inside the answer since the night she started.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Wait for him to see you.">><p>He looks up. She stands in the middle of his floor holding four empty glasses and lets him.</p><p>In all that time in the cell he never once looked at her face. He looked at the light under her sternum, and he wrote what it said in a notebook, and the light is out now.</p><span id="cascade-b2-beat-7"><<linkreplace "Let him look.">><p>So what is standing in his bar is a woman with a tray and somebody else's face, and what he is remembering, if he is remembering anything at all, is a number in a column.</p><p>He looks at her the way a man looks at a chair he is thinking of moving. Then he looks back down at the desk.</p><span id="cascade-b2-beat-8"><<linkreplace "Finish the tables.">><p>The door shuts a minute later, from the inside, without anybody crossing to it.</p><span id="cascade-b2-beat-9"><<linkreplace "Take the glasses back.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> You're on the taps from tomorrow.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Says who.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Says the rota. Don't make it a conversation — I've been doing this thirty years and the rota's never once been my idea.</div></div><p>She works the rest of the shift and her hands stay steady the whole way through it, which is a thing she notices and does not examine.</p><p>At closing she walks up the strip in somebody else's coat, past the market, past the gate, down to a dock where a man is cutting a hull open under a lamp, and she does not tell him either.</p>[[Go home and say nothing.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_bastien_notices">>
<<set $game_state.current_node = "cap_bastien_notices.base">>
<<script>>setup.markCanvasTriggered("cap_bastien_notices");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_bastien_notices.base") === -1) { vn.push("cap_bastien_notices.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "bar_rung", "set", 2.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "bastien_seen", "set");<</script>><img src="./videos/locations/underworld_bar.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Second night at the same stool. He rings her drink without being asked this time, which down here is most of a conversation.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Are you hiring.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Always. It's that kind of room.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Wait for the terms.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Glasses, tables, and you carry a full tray down three steps without looking at your feet. Fifteen a shift and you keep whatever's left on the bar, which is nothing, because the men in here are careful with money.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Do the arithmetic out loud.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The House pays thirty a head.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> The House pays thirty a head. So go and work at The House.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Don't.">><p>Thirty a head buys the cot and the line and a part, and she has been doing exactly that arithmetic since the night Kess first put her on the feed line, and every answer it ever gave her belonged to somebody else's errand.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Fifteen a shift, six nights a week, standing in a room with a door in it. That is not what the money is for.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Take it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Fifteen's fine.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Course it is. You've sat there and not once tried to tell me anything about yourself, and that's the whole interview.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Ask who you'd be working for.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Whose bar is it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Mine, six nights out of seven. Somebody's on the paper. He's never once come down to look at it and I've never once gone up to ask.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Push a little.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've never met him.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> I've outlasted four owners. You don't do that by learning which one you've got. An owner you never see is an owner who never asks you a question you can't answer.</div></div><span id="cascade-b2-beat-7"><<linkreplace "Ask about the back.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> And that door's not yours. Men use that room. They come in the alley side and they go out the alley side and what they want from this bar comes to them, it doesn't get fetched.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> If you're ever in there it'll be because somebody sent for you, and I'd think hard about that night before you got to it.</div></div><p>He puts a bar cloth on the counter in front of her, which is the whole of the paperwork, and goes back down the taps.</p><p>She ties her hair back with somebody else's hands and starts on the glasses at the near end, where she can see the door.</p>[[Start on the glasses.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_sol_hires">>
<<set $game_state.current_node = "cap_sol_hires.base">>
<<script>>setup.markCanvasTriggered("cap_sol_hires");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_sol_hires.base") === -1) { vn.push("cap_sol_hires.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "bar_rung", "set", 1.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "bar_hired", "set");<</script>><img src="./videos/scenes/wren_stranger_bar.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The Undertow at nine. Same door, same three steps down, same smell of wet coats and spilled beer over old smoke.</p><span id="cascade-b2-beat-0"><p>The room does what the room does when anybody comes in: a half-second of attention, priced and dismissed. She has been getting a different half-second than that since the first night she came down those steps, and had not noticed until it stopped.</p><span id="cascade-b2-beat-1"><<linkreplace "Go to the bar.">><p>Sol comes down the counter with the towel over his shoulder and the flat, even, entirely pleasant face he keeps for people whose names he does not have yet.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> What'll it be.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Order something.">><p>She orders the thing she always orders. He pours it, takes her coin, and rings it, and there is nothing at all in it.</p><span id="cascade-b2-beat-3"><<linkreplace "Wait for it.">><p>He does not lean. He does not do the thing where he finds a glass to wipe near her. He does not say the short flat sentence about somebody who died on the strip last week that he has been saying to her, unasked, since the second week.</p><span id="cascade-b2-beat-4"><<linkreplace "Hold still.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> It works. That is the finding, and it is the finding she paid for. A man who decided a long time ago that he liked her has looked at her twice tonight and got nothing back either time.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Look at the back door.">><p>There is a door at the back of the room with no handle on this side. She has walked past it more times than she could count and never once wondered about it, which she now thinks is itself a strange thing to be true.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> That's not a door you look at, love. New ones always do, mind. It's how I know they're new.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Say nothing much.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Just came in out of the rain.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> It's not raining.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Doesn't matter to me. Everybody comes in out of something.</div></div><p>She takes the drink to the end of the bar and sits where she can see the door with no handle, and stays until closing, and nobody asks her anything at all.</p>[[Stay till closing.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_stranger_at_the_bar">>
<<set $game_state.current_node = "cap_stranger_at_the_bar.base">>
<<script>>setup.markCanvasTriggered("cap_stranger_at_the_bar");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_stranger_at_the_bar.base") === -1) { vn.push("cap_stranger_at_the_bar.base"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "stranger_seen", "set");<</script>><img src="./videos/locations/underworld_strip.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The strip at the hour when it fills. She stands out of the traffic with her back to a wall and does the arithmetic properly instead of walking it off again.</p><span id="cascade-b2-beat-0"><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Four people. One question. A courier who protects himself, a madam who deals straight, a barman who likes her, and a stall that would sell her a person. Four different answers and the same shape underneath all of them.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Count it again.">><p>Any one of them alone is a man's own business. Rue on her own is a bad week. Sol on his own is a man with a headache.</p><p>Three of them, one after another, is not three private decisions. Somebody said something, and every one of them heard it before she asked.</p><span id="cascade-b2-beat-2"><<linkreplace "Ask who.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> She took a thing out of her own spine that a company put there, and she has been walking around ever since like a woman who got away with it.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Follow it.">><p>The Reach does not close for nothing. It closes for money it is frightened of losing, and there is exactly one outfit above this water with enough of that to buy a whole street's manners at once.</p><span id="cascade-b2-beat-4"><<linkreplace "Test it.">><p>She tests it against what she saw. Nobody was frightened of her. Nobody threatened her, took her coin, or asked her to leave. Rue offered her work at the same rate as always.</p><p>That fits. A word does not have to say harm her. A word only has to say her name and not one thing more, and careful people fill in the rest themselves.</p><span id="cascade-b2-beat-5"><<linkreplace "Take the consequence.">><p>So the question is dead. Not the buyer — the buyer is still down here with a stack of paper and a habit of paying on time. Dead is her asking it.</p><span id="cascade-b2-beat-6"><<linkreplace "Think about the covers.">><p>She has done this before. Coveralls at a depot, an analyst's kit on a Vance floor, a walk and a voice and a name for each. She was good at it. It is the one thing they built her to be good at.</p><p>Both times Mercer handed her the clothes and told her whose life to get inside.</p><span id="cascade-b2-beat-7"><<linkreplace "Follow that.">><p>Whatever word is out, it has a name in it, and the name is Wren. It does not describe a woman off a boat with no history and nothing to trade but hours.</p><p>There is a row at the far end of the market where the stock has faces. Whatever they sell down there to make a person stop being findable, she can afford it or she can earn it.</p>[[Go and price it.->Location_underworld_strip]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_the_wall">>
<<set $game_state.current_node = "cap_the_wall.base">>
<<script>>setup.markCanvasTriggered("cap_the_wall");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_the_wall.base") === -1) { vn.push("cap_the_wall.base"); }<</script>>
<<script>>advanceTime(45);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "wall_understood", "set");<</script>><p>The paper stall is the old man with the new name, and the price of a thing here has never once depended on what the thing was.</p><span id="cascade-b1-beat-0"><p>She puts coin on the counter before she asks, because that is the grammar down here and it has never failed.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Company paper. Somebody's been buying it for years. I want where it goes.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>He looks at the coin the way a man looks at weather. He does not touch it, and not touching it takes him some effort.</p><p>"Not that," he says. "Anything else on this table and half of what's under it."</p><span id="cascade-b1-beat-2"><<linkreplace "Add to it.">><p>She adds to it. It is more than the manifests behind him are worth and they both know it.</p><p>"Still not that."</p><span id="cascade-b1-beat-3"><<linkreplace "Ask why not.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You sell people off the end of this row.</div></div><p>"I do. Take your money back, love — I'd rather you had it than left it here looking at me."</p><p>She takes it back. Four people now, and every one of them has found a different way to be perfectly reasonable at her, and not one of them has said no.</p>[[Pocket it.->Location_underworld_market]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "wall_market">>
<<set $game_state.current_node = "wall_market.base">>
<<script>>setup.markCanvasTriggered("wall_market");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("wall_market.base") === -1) { vn.push("wall_market.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "wall_refusals", "add", 1.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "wall_market_asked", "set");<</script>><p>Sol has the taps and the towel and the flat attention he gives everyone, and she has spent every night since she came down the gate earning a version of it that is not flat.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Somebody down here collects Vance paper. Years of it. You'd know who.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>He wipes the same clean glass he was already wiping. He is a man who has told her true things about dead men and where their people ended up, unprompted, for free.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> I would.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Wait for the rest.">><p>There is no rest. He sets the glass down and picks up another one that is also already clean.</p><span id="cascade-b1-beat-3"><<linkreplace "Ask him straight.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've never made me ask twice.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> No.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> I'll pour for you all night and I'll not charge you for the last one. That's what I've got.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Push it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Sol.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Don't. You'll only make me say something short and then we'll both have to live in this bar afterwards.</div></div><p>He is not frightened of her and he is not ashamed. He looks like a man keeping a promise he does not enjoy having made, and she has no idea who he made it to.</p>[[Let it go.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "wall_sol">>
<<set $game_state.current_node = "wall_sol.base">>
<<script>>setup.markCanvasTriggered("wall_sol");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("wall_sol.base") === -1) { vn.push("wall_sol.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "wall_refusals", "add", 1.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "wall_sol_asked", "set");<</script>><p>Rue is doing the book, which she does in the open, because the whole of her authority down here is that her numbers are the same numbers whoever asks.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Who buys company paper on the strip. Not the fences. The one who keeps it.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Wait for it.">><p>The pen stops. It is a short stop and it is the first time since the madam took her on that Rue has taken any time at all to answer her.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> That's not a thing I trade in.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Say the obvious.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You trade in who's where and who pays. That's the same trade.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> It is. And I'm still not trading in it, and I'd rather you took that as an answer than as the start of something.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Ask what changed.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've never done that to me.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> No. And there's work upstairs at thirty a head if you want it, same as always, and I'll count it out honest, same as always.</div></div><p>The offer is real. Everything about it is exactly as straight as Rue has ever been with her, and it is being used to change the subject.</p>[[Leave her to the book.->Location_underworld_brothel]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "wall_rue">>
<<set $game_state.current_node = "wall_rue.base">>
<<script>>setup.markCanvasTriggered("wall_rue");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("wall_rue.base") === -1) { vn.push("wall_rue.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "wall_refusals", "add", 1.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "wall_rue_asked", "set");<</script>><p>He is in the corner he is always in, back to the wall, drinking at the speed of a man who intends to still be upright at closing.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The copies you carried out of Vance. Who was at the other end.</div></div><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>He does not put the glass down, which is his tell, and she has seen it enough times now to read it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> You know what I'm going to say. You've heard me say it about other people and you liked me for it.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Ask anyway.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I'm asking anyway.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> I don't learn a name I don't need. I've never needed that one. I put a package where I'm told and I go home, and that's thirty years of still being here.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Push.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You know the room. You've stood outside it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> I've stood outside a lot of rooms. Ask me about the ones with nothing in them and I'll talk all night.</div></div><p>That is not a man hiding something. That is a man who arranged his whole life so there would be nothing to hide, and she cannot hold it against him.</p>[[Let him drink.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "wall_colm">>
<<set $game_state.current_node = "wall_colm.base">>
<<script>>setup.markCanvasTriggered("wall_colm");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("wall_colm.base") === -1) { vn.push("wall_colm.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "wall_refusals", "add", 1.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "wall_colm_asked", "set");<</script>><img src="./videos/scenes/kess_chip_light.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The lamp is down over the bench this time, tight on one small grey thing, and the tools beside it are the fine ones.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You left it. I said I'd rather not know and I meant that too, and then it sat there looking at me.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Ask what he did.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've had it under a light.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I've had it under two. Don't get excited — I can tell you how it was made and not one thing about what it did.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Take what there is.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's about fifteen years old. That's not a guess, that's the finish on it.</div></div><p>Her frame is nine. The number on her line is thirteen. The thing that was holding her shut is older than both.</p><span id="cascade-b2-beat-3"><<linkreplace "Ask how he can tell.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> From the finish.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> From the tooling. Everything's cut a shade wide and then dressed back by hand, which is what you do when you're making eleven of a thing and not eleven thousand.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Let him finish.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And nobody's dressed a part by hand in this city for ten years. Whoever made that had a room, a budget and no hurry.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Ask him to open it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Can you get inside it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> No. Not won't — can't. I cut ships. What's in there wants a clean room and somebody who's seen one before, and there's neither of those within forty miles of this water.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Ask what would.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Paper would.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Your page came open because the two were cut together. That's not a trick I did, that's the same office signing twice. Find me the rest of what that office wrote and I'll have something to work against.</div></div><span id="cascade-b2-beat-7"><<linkreplace "Ask where that lives.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where does an office's paper go.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Not my trade. But paper that's worth anything gets sold, and this city's got exactly one direction it gets sold in, and it isn't up.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Think.">><p>She has met that direction. A careful man in a corner of the Undertow, drinking slow, carrying copies out of Vance Securities and down the strip to somebody who paid on time.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> There was a courier. I never found out who was buying.</div></div><span id="cascade-b2-beat-9"><<linkreplace "Say it out loud.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Then that's your end of it, and it's a better end than mine. A buyer keeps what he buys.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Somebody down there has been collecting that company's paper for years. If your page had a brother, it's in his stack.</div></div><span id="cascade-b2-beat-10"><<linkreplace "Ask if he'll help.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I'll build. That's the arrangement and I've not changed it. You bring me a thing that needs making and I'll price it, and I'll not ask what it's for.</div></div><span id="cascade-b2-beat-11"><<linkreplace "Nothing to say to that.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Two years of being told where to stand and by whom. Nobody has told her to do this. There is no order behind it, no dispatch, no man upstairs waiting on a result — only a shut page and a hand that closed it, and the fact that she wants to know.</div></div><span id="cascade-b2-beat-12"><<linkreplace "Reach for the chip.">><p>Her hand goes to it and stops. Wherever this goes, it goes through people who put hands in her coat, and there is nothing on her worth less explaining than this.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Keep it on the bench.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It'll be under the jar. It's been under the jar since you left it and the jar's not moved.</div></div><p>She goes up the strip with nothing in her pockets and one question in her, which is more than she has ever carried out of this dock.</p>[[Go and find the buyer.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_chip_read">>
<<set $game_state.current_node = "cap_chip_read.base">>
<<script>>setup.markCanvasTriggered("cap_chip_read");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_chip_read.base") === -1) { vn.push("cap_chip_read.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "chip_read", "set");<</script>><img src="./videos/scenes/mercer_panic.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She comes up to the penthouse with the file to close the job — and finds Mercer on the wrong side of his own desk, a bag open on the glass, feeding paper into a shredder that can't keep up.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It's done. Deep vault, clean pull. Everything you sent me in there for.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Get it away from me. God — do you have any idea what you kicked over? A deep-vault flag with my asset's fingerprints on it, and it is climbing, and it does not stop until it reaches Vance himself.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Watch him unravel.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You wanted this file for a year. Now you won't touch it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I wanted it when it was a clever little edge. It's a noose now. The Chairman doesn't discipline, he disappears people — I've watched him do it. I will not be standing here when he comes asking who ran an asset into his archive. Keep the file, burn it, I don't care. I need to be gone, and every road out of this tower has his people on it by now.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Offer him the one road he hasn't got.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Not every road. There's one place his people can't follow, because they don't know it's there. Under the docks. Under the Reach. I can walk you into it tonight and nobody topside ever sees where you went.</div></div><p>That stops his hands. He looks at her the way a drowning man looks at a rope he doesn't trust — because he knows her, knows nothing she offers is free.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> ...And what does that cost me.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Name it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The controller. The one for the governor you had sealed into my core — the little key that decides who I can't turn my teeth on. You've carried it since the day you signed for me. Give it to me and I take you down alive. That's the fare.</div></div><p>For a second the fear thins and something like a smile moves under it — the reflex of a man who's spent his life sure he holds the only card that matters.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> That's it? Take it. It's keyed to me, girl — my hand, my print, it's a dead lump of metal to anyone else alive. You can wear it on a chain. It won't cut a thing.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Take the key. Take him down.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> He's wrong, and he'll die never knowing how wrong. Keyed to him, sure — until a man who takes machines apart for a living gets it under a light. She closes her hand around it and it's the first thing in two years that's hers and about her at once. The file in her coat, the key in her fist. Both locked. Both hers.</div></div><p>She gets him into the car and gives the driver an address that isn't on any manifest. The tower falls away, the city goes lower and wetter, and the Spire seals itself shut behind them — sweeps on every floor now, her face on every camera at the curb. No ride back up. At the bottom, where the last light gives out, Mercer goes his own way into the dark to find whatever hole a running man finds, and doesn't look back to thank her. The deal's paid; they're quits. She turns the other way — toward the one door down here that owes her nothing and might still help. Kess.</p>[[Down under the city — go to Kess.->Location_underworld_strip]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_1a_close">>
<<set $game_state.current_node = "cap_1a_close.base">>
<<script>>setup.markCanvasTriggered("cap_1a_close");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_1a_close.base") === -1) { vn.push("cap_1a_close.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "archive_1a_done", "set");setup.applyAndNotifyFlag("player", null, "controller_held", "set");<</script>>
<<script>>setup.pendingEffects = setup.pendingEffects || [];setup.addToWardrobe("company_grays"); setup.equipItem("company_grays");setup.showEffectNotification();<</script>><p>She catches Vane on the back stairs, where the cameras don't reach and Calloway never goes, and doesn't waste the opening on hello.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> A courier at the Undertow gave you up. Not your name — he never had it — your face. Handing him sealed pulls at the loading dock, month after month. Two years Calloway's chased his ghost, and it's been at the next desk the whole time.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> You don't— that's nothing. That's a drunk in a bar, you've got a face and a feeling and no—</div></div><span id="cascade-b1-beat-1"><<linkreplace "Show him the drop.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I've got the courier, the dock, the dates, and a very tired auditor upstairs who'd love a name that isn't Calloway's. I don't want you, Vane. I want one thing out of that vault, and you're cleared for it and I'm not.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> ...Christ. The deep archive? Do you know what they do to— no. I move paper. I don't touch the vault, that's a different—</div></div><span id="cascade-b1-beat-2"><<linkreplace "Make it the only door left.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You pull my file, or the auditor gets your face by morning and it climbs to the Chairman by noon. You know what he does with a leak. My way, you're a man who did a quiet favour. The other way, you're an example.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> ...One file. One. And then I have never met you.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Wait for the file.">><p>It takes him two days and a clearance that isn't his to use. He brings it to the same dead stairwell — a thin sealed folder — and his hands aren't steady.</p><span id="cascade-b1-beat-4"><<linkreplace "Take the file.">><p>She has it: the thing Mercer sent her for, and the thing she's wanted in her own hands since she learned it was in this building. A Vance humanoid-program file — about her kind, maybe about her. Light enough to be nothing. She doesn't open it. Not here.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> Don't read it here. And listen — pulling that tripped something. There's a flag on the deep-vault log I can't clear, and somebody's already asking who signed it out.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Ask who's watching.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Calloway's watcher. He never trusted me either.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> Whoever it is, it climbs — and it climbs to the man who ends people for less than this. I'm gone tonight. The Reach, then further. You should be too. What you just did isn't the kind of thing you get to stay for.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> So it's blown. Fine. She has the file and a night's head start, and a man who ends betrayers is now aimed at everyone who touched this — Vane, and the fool up the chain who sent her in. She'd better reach Mercer before the news does.</div></div>[[Get to Mercer.->Location_penthouse]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_vane_blackmail">>
<<set $game_state.current_node = "cap_vane_blackmail.base">>
<<script>>setup.markCanvasTriggered("cap_vane_blackmail");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_vane_blackmail.base") === -1) { vn.push("cap_vane_blackmail.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "docs_retrieved", "set");<</script>><p>She comes back after hours, when the department runs on a skeleton and the floor above is dark — and there's Vane, badge on a lanyard, walking the vault door open like it's nothing. The reader goes green for him. A guard nods him through.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Of course. Quiet, tidy, trusted Vane — senior custody, cleared for the one door that matters, in and out on his own routine while the division dies around him. Whatever she needs out of that vault doesn't come out without a man exactly like him.</div></div>
<<set $game_state.current_canvas = "cap_docs_vane_routine">>
<<set $game_state.current_node = "cap_docs_vane_routine.base">>
<<script>>setup.markCanvasTriggered("cap_docs_vane_routine");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_docs_vane_routine.base") === -1) { vn.push("cap_docs_vane_routine.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Watch him work.->Location_docs_department]]<img src="./videos/scenes/docs_department.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Calloway gave it up under the drain — the deep vault, down here — so she comes to see it for herself. The docs department is everything his file room isn't: glass, logged, watched. This is where the files that matter actually live, and where whatever Mercer planted her to fetch is sitting, behind a door with her name nowhere near the list.</p><span id="cascade-b2-beat-0"><p>She badges toward the vault like she belongs there. The reader blinks red. A guard steps half a pace to fill the gap — not even unfriendly about it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/enns.jpg" alt="Enns" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Enns:</strong> That's two-man clearance, love. You're not on the list, and I can't put you on it — nobody at your level goes past me. Anything you need out of the deep archive, you file a pull and someone cleared brings it up.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Ask who's cleared.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> And who's cleared?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/enns.jpg" alt="Enns" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Enns:</strong> A handful. Senior custody only. They rotate the second man weekly so nobody can walk it out alone. It's the one part of this dying place they still take seriously.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> So there's the wall. Her file is ten feet away behind a door her badge will never open, and the only way through is somebody already on the inside. She'll need one of the cleared few — which means she needs to know exactly who they are, and find the right thing to hold over one.</div></div>[[Back out.->Location_docs_department]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_docs_first_look">>
<<set $game_state.current_node = "cap_docs_first_look.base">>
<<script>>setup.markCanvasTriggered("cap_docs_first_look");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_docs_first_look.base") === -1) { vn.push("cap_docs_first_look.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "access_problem_seen", "set");<</script>><p>The whole compartmented, careful little mind of him comes loose down her arm while he's still shaking — routes and drops and hand-offs, months of them, every pickup he's ever made.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> There. The inside contact. Not a name — Colm was never trusted with a name — a FACE, handing him a sealed packet at a loading dock, month after month. Narrow, nervous, careful. The face she sits three feet from at Calloway's elbow every single day. Vane. It's Vane.</div></div><p>She leaves him face-down and snoring on the empties, his route still spilling out of him into nobody, and walks back up into the night with the one thing she came for. She has the rogue now. She just has to make him hand her the vault.</p>
<<set $game_state.current_canvas = "colm_drain_canvas">>
<<set $game_state.current_node = "colm_drain_canvas.d0">>
<<script>>setup.markCanvasTriggered("colm_drain_canvas");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("colm_drain_canvas.d0") === -1) { vn.push("colm_drain_canvas.d0"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Go collect on Vane.->Location_underworld_bar]]<p>The drain fires the way it does now — no discovery left in it, just his Charge coming loose down her arm and the night going soft behind his eyes. He'll wake on the empties grateful and none the wiser, sure he had the best night of his week. In a way he did.</p>
<<set $game_state.current_canvas = "colm_drain_canvas">>
<<set $game_state.current_node = "colm_drain_canvas.done">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("colm_drain_canvas.done") === -1) { vn.push("colm_drain_canvas.done"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave him to it.->Location_underworld_bar]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<p>He finishes on her face with one hand shaking in her hair, apologising while he does it. Nothing comes loose but him. The drain stays shut — it only opens the one way.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_finish_facial_t5"] to ($game_state.media_cycle["sex_colm_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_finish_facial_t5/c8b2eddc397.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_finish_facial_t5/cab07d5fbd0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_finish_facial_t5/ce6086edb85.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_finish_facial_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<p>He finishes inside her, holding her up against the shelving, breathing like a man who's just been given something. He has been. She hasn't taken anything — not this way.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_finish_inside_t5"] to ($game_state.media_cycle["sex_colm_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_finish_inside_t5/c5bae15ed64.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_finish_inside_t5/c293c32f790.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_finish_inside_t5/cb853fc000c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_finish_inside_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<p>He finishes in her ass, and the drain opens under it the way it's built to — the shudder cracking him wide, her hand still locked on his forearm, everything he is starting to come loose down her arm.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_finish_anal_t5"] to ($game_state.media_cycle["sex_colm_finish_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_finish_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_finish_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_finish_anal_t5/c1bd134c597.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_finish_anal_t5/c67bb5babaa.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_finish_anal_t5/c79e5de0c9c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_finish_anal_t5/c037160e284.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>>
<<set $game_state.current_canvas = "loop_colm_finisher">>
<<set $game_state.current_node = "loop_colm_finisher.climax">>
<<script>>setup.markCanvasTriggered("loop_colm_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_colm_finisher.climax") === -1) { vn.push("loop_colm_finisher.climax"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_colm_finisher:cc0') ? '' : 'unlocked-choice'">
<<link "Leave him to the empties." "Location_underworld_bar">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_finisher:cc0");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_colm_finisher:cc1') ? '' : 'unlocked-choice'">
<<link "Take what he carries." "Canvas_colm_drain_canvas_Node_d0">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "colm_drains_done", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_finisher:cc1");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_colm_finisher:cc2') ? '' : 'unlocked-choice'">
<<link "Drink him down again." "Canvas_colm_drain_canvas_Node_done">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_finisher:cc2");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "gte", "value": 1}]})))>>
<<script>>
try {
window._engineNoExitsDiag_loop_colm_finisher = [{"text": "Leave him to the empties.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]}}, {"text": "Take what he carries.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "lt", "value": 1}]}}, {"text": "Drink him down again.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "gte", "value": 1}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "loop_colm_finisher",
choices: window._engineNoExitsDiag_loop_colm_finisher,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_loop_colm_finisher>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas loop_colm_finisher</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Navigation]]
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_backroom_t4"] to ($game_state.media_cycle["sex_colm_backroom_t4"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_backroom_t4"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_backroom_t4"]>><<if _mc is 0>><video src="./videos/sex/colm_backroom_t4/c06cf533433.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_backroom_t4/c898bb972fa.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_backroom_t4/ca055246f57.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_backroom_t4/ccbe4fd9c16.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>The back room is empties and stacked cases and one bad bulb. He has her against the shelving before the door's shut — mouth on her neck, hands going everywhere at once, a careful man with nothing careful left in him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> Three weeks I've been telling myself you were being kind. Tell me to stop and I'll stop. Don't, and I won't.</div></div>
<<set $game_state.current_canvas = "loop_colm_backroom">>
<<set $game_state.current_node = "loop_colm_backroom.intro">>
<<script>>setup.markCanvasTriggered("loop_colm_backroom");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_colm_backroom.intro") === -1) { vn.push("loop_colm_backroom.intro"); }<</script>>
<<nobr>>
<<link "Get on your knees." "Canvas_loop_colm_backroom_Node_base_oral_colm">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_loop_oral_t5"] to ($game_state.media_cycle["sex_colm_loop_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_loop_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_loop_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_loop_oral_t5/c2ef1812571.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_loop_oral_t5/c9b02864509.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_loop_oral_t5/c3211d67c7f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_loop_oral_t5/cc475989d63.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees on the concrete with his cock in her mouth, both his hands braced on the case behind him. He watches the door out of thirty years of habit, and loses the habit fast.</p>
<<set $game_state.current_canvas = "loop_colm_backroom">>
<<set $game_state.current_node = "loop_colm_backroom.base_oral_colm">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_colm_backroom.base_oral_colm") === -1) { vn.push("loop_colm_backroom.base_oral_colm"); }<</script>>
<<nobr>>
<<link "Keep him in your mouth." "Canvas_loop_colm_backroom_Node_base_oral_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Stand up and put him in you." "Canvas_loop_colm_backroom_Node_base_vaginal_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_colm_backroom:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_loop_colm_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_backroom:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_loop_vaginal_t5"] to ($game_state.media_cycle["sex_colm_loop_vaginal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_loop_vaginal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_loop_vaginal_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_loop_vaginal_t5/c3b4cff62ef.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_loop_vaginal_t5/c7b8c94db63.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_loop_vaginal_t5/c293c32f790.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_loop_vaginal_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She takes him standing, one leg hooked up on a stacked case, his cock working into her cunt while the bar noise comes through the wall. He says her false name into her hair like it's a real one.</p>
<<set $game_state.current_canvas = "loop_colm_backroom">>
<<set $game_state.current_node = "loop_colm_backroom.base_vaginal_colm">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_colm_backroom.base_vaginal_colm") === -1) { vn.push("loop_colm_backroom.base_vaginal_colm"); }<</script>>
<<nobr>>
<<link "Let him have it." "Canvas_loop_colm_backroom_Node_base_vaginal_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_loop_colm_backroom_Node_base_oral_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn around — give him your ass." "Canvas_loop_colm_backroom_Node_base_anal_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_colm_backroom:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_loop_colm_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_backroom:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_colm_loop_anal_t5"] to ($game_state.media_cycle["sex_colm_loop_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_colm_loop_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_colm_loop_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/colm_loop_anal_t5/c7e7b540eff.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/colm_loop_anal_t5/c3748c12b3c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/colm_loop_anal_t5/cb45bddb482.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/colm_loop_anal_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent over the stacked cases with his cock in her ass, his hands white-knuckled on her hips. He's forgotten the door, the room, the job — everything but the fact that she let him.</p><p>She reaches back and gets a hand around his forearm, skin on skin, and holds it there. That's the seat. The drain sits in the contact and waits for the shudder to open it.</p>
<<set $game_state.current_canvas = "loop_colm_backroom">>
<<set $game_state.current_node = "loop_colm_backroom.base_anal_colm">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_colm_backroom.base_anal_colm") === -1) { vn.push("loop_colm_backroom.base_anal_colm"); }<</script>>
<<nobr>>
<<link "Take it in the ass." "Canvas_loop_colm_backroom_Node_base_anal_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back on him." "Canvas_loop_colm_backroom_Node_base_vaginal_colm">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_colm_backroom:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — and take what he carries." "Canvas_loop_colm_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_colm_backroom:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><img src="./videos/scenes/colm_undertow.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_false"}]})>>
<p>Three stools down: the man Sol pointed her to. Corner seat, back to the wall, a bag with a boot hooked through the strap so it never leaves his ankle. He's nursing one drink like it has to last till dawn, and his eyes go to the door on a clock she can't hear — every ninety seconds, reflex, the tic of a man who's counted the exits in every room he's ever sat in. She takes the stool beside him. He clocks her, weighs her, and gives her exactly nothing — polite, careful, a man having a quiet one and meaning to keep it that way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> If you're selling, I'm not buying. If you're asking, I'm that even less. Nothing personal — just how I've stayed a man who gets to keep having quiet ones.</div></div><p>But he doesn't move stools, and he doesn't tell her to leave. He just goes back to watching the door, letting her sit inside the arm's length he gives nobody — which is its own kind of answer. Careful men notice careful people. She hasn't asked him for a single thing; that's the only reason she's still got the seat.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Careful to a fault, exactly like Sol said — and lonely with it, under all that guard. You don't crack a man like this with a question. You crack him with time. The first hour starts with a drink he didn't have to pay for.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 12}]})>>
<p>He's still all business, but he lets her take the stool now without the once-over. A careful man testing the shape of her — what she wants, why a woman like her keeps spending her evenings on a courier three seats from the door.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> Nobody buys a man this many rounds for the conversation. So what is it — you want something moved, or you want me?</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 12}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 24}]})>>
<p>He saves her the stool now. The guard's not gone — a careful man's never fully goes — but he's loose, warm, leaning in to hear her over the room. He watches the door out of habit and her out of want.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> You're the best part of my week, you know that. Whatever it is you actually came for, I'm starting not to care.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 24}]})>>
<p>The careful's all the way off him now. He's up off the stool before she's fully in the door, hand at the small of her back, steering her to the dark end of the bar like the room's already theirs. He'll follow her anywhere she points him — and she knows exactly where that is.</p>
<</if>>
<<set $game_state.current_canvas = "hub_colm_undertow">>
<<set $game_state.current_node = "hub_colm_undertow.base">>
<<script>>setup.markCanvasTriggered("hub_colm_undertow");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_colm_undertow.base") === -1) { vn.push("hub_colm_undertow.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc0') ? '' : 'unlocked-choice'">
<<link "Can I buy you a drink?" "Canvas_hub_colm_undertow_Node_meet_drink">><<script>>setup.markChoiceVisited("hub_colm_undertow:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc1') ? '' : 'unlocked-choice'">
<<link "Not tonight." "Location_underworld_bar">><<script>>setup.markChoiceVisited("hub_colm_undertow:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc2') ? '' : 'unlocked-choice'">
<<link "Pass the time with him." "Canvas_hub_colm_undertow_Node_talk">><<script>>setup.markChoiceVisited("hub_colm_undertow:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc3') ? '' : 'unlocked-choice'">
<<link "Buy him another." "Canvas_hub_colm_undertow_Node_drink">><<script>>setup.markChoiceVisited("hub_colm_undertow:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 12}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc4') ? '' : 'unlocked-choice'">
<<link "Kiss him." "Canvas_hub_colm_undertow_Node_kiss">><<script>>setup.markChoiceVisited("hub_colm_undertow:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "colm_met", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 24}]})>>
<span @class="setup.isChoiceVisited('hub_colm_undertow:cc5') ? '' : 'unlocked-choice'">
<<link "Take him in the back." "Canvas_loop_colm_backroom_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_colm_undertow:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave him to his drink." "Location_underworld_bar">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She flags Sol for two and slides one in front of him before he can wave it off. Colm looks at the glass, looks at her, and does the arithmetic of a man who trusts nothing free — what it costs him to drink it against what it costs him to refuse. Refusing is the louder tell. He drinks.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> ...Appreciated. Doesn't buy you anything but the drink, understand. But — appreciated.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> No sell, no ask. Just the only man in here who looks like he'd still be alive this time next year — and I'm tired of drinking alone.</div></div><p>That earns her a look, a real one, held a beat longer than he means to give it — two careful people clocking the same wariness in each other. He says nothing else worth keeping. But he lets a whole minute pass without checking the door, and for a man like Colm that's most of a confession.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> One drink in and he's still all edges. Fine — she's got nowhere to be and he's here every night. Somewhere under the careful there's a courier who moves things between the worlds for a buyer who pays on time. That's the thread. She just has to be patient enough to pull it, a round at a time.</div></div>
<<set $game_state.current_canvas = "hub_colm_undertow">>
<<set $game_state.current_node = "hub_colm_undertow.meet_drink">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_colm_undertow.meet_drink") === -1) { vn.push("hub_colm_undertow.meet_drink"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_colm", "relation", "add", 3.0, false, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "colm_met", "set");<</script>>
[[Let him nurse it. Come back.->Location_underworld_bar]]<<set _bp to random(0, 2)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> You want to know how you last down here? You never carry what you can't drop and you never learn a name you don't need. I've forgotten more people than this bar's seen. On purpose.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> Buyer up top pays on time, never asks how, never says thanks. Best kind of work there is — the kind nobody wants to be caught talking about, me included. So we won't.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> Sol vouches for exactly nobody, and he lets you sit here. That's the only reference in this room worth a damn, and it's the only reason I haven't moved stools.</div></div>
<</if>><p>He talks around the work, never through it — but he talks, which for Colm is a door coming open a finger's width at a time. Each hour she spends not asking him for anything is an hour he trusts her a little more.</p>
<<set $game_state.current_canvas = "hub_colm_undertow">>
<<set $game_state.current_node = "hub_colm_undertow.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_colm_undertow.talk") === -1) { vn.push("hub_colm_undertow.talk"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_colm", "relation", "add", 2.0, false, null);setup.showEffectNotification();<</script>>
[[Let it ride.->Location_underworld_bar]]<p>Another round, and the night wears on the way nights do down here — slow, red-lit, the room thinning around them. He drinks careful, always careful. But he drinks with her now, which for a man who's made a life of drinking alone is not nothing.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 12}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> I don't get sloppy — never have, not the job for it. But I'll drink what you're pouring. Beats the wall, and you don't talk half as much as the others who try to sit here.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 12}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 24}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> You're turning into a habit. Only good kind there is, in my line — the sort nobody's supposed to know I've got. Don't make me regret it.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 24}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> Keep buying and I keep forgetting to watch that door. Reckon you know that. Reckon that's the point — and I've stopped minding either way.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_colm_undertow">>
<<set $game_state.current_node = "hub_colm_undertow.drink">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_colm_undertow.drink") === -1) { vn.push("hub_colm_undertow.drink"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_colm", "relation", "add", 3.0, false, null);setup.showEffectNotification();<</script>>
[[One more, then.->Location_underworld_bar]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 16}]})>>
<p>She leans in and kisses him, slow, in the middle of the crowded bar. He goes stiff — a careful man's whole body saying not here, not smart, not safe — and pulls back an inch before he lets himself have even a second of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> —not here. People see. You don't know who drinks in this room.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 16}, {"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "lt", "value": 20}]})>>
<p>This time he doesn't pull back as far. His hand comes up to her jaw like it decided on its own, and the careful takes a beat longer to win. He's warming — she can feel exactly where the guard thins.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/colm.jpg" alt="Colm" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Colm:</strong> You're going to get me killed. ...Do it again.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_colm", "trait_key": "relation", "operator": "gte", "value": 20}]})>>
<p>Now he stops flinching entirely. He kisses her back like the room isn't there, one hand fisted in her coat, the careful drinker gone under a want he isn't managing anymore.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> There. The guard's off. One more lean and he'll follow her anywhere she points him — including the back, where the drain likes to work.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_colm_undertow">>
<<set $game_state.current_node = "hub_colm_undertow.kiss">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_colm_undertow.kiss") === -1) { vn.push("hub_colm_undertow.kiss"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_colm", "relation", "add", 4.0, false, null);setup.showEffectNotification();<</script>>
[[Let him breathe.->Location_underworld_bar]]<img src="./videos/scenes/underworld_bar_bartender.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Sol works the taps at the dim end of the bar, wiping a glass that's already clean. He's outlasted every owner this place has had — and it's had a few, though the one it answers to now never shows his face. He hears everything said over his counter down here and repeats none of it, unless you're the kind he's decided he likes. He's decided he likes her. Mostly.</p>
<<set $game_state.current_canvas = "hub_sol_undertow">>
<<set $game_state.current_node = "hub_sol_undertow.base">>
<<script>>setup.markCanvasTriggered("hub_sol_undertow");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_sol_undertow.base") === -1) { vn.push("hub_sol_undertow.base"); }<</script>>
<<nobr>>
<<link "Pass a word with Sol." "Canvas_hub_sol_undertow_Node_talk">><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "pipe_known", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "colm_found", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('hub_sol_undertow:cc0') ? '' : 'unlocked-choice'">
<<link "Ask who moves things between the worlds." "Canvas_hub_sol_undertow_Node_carrier">><<script>>setup.markChoiceVisited("hub_sol_undertow:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "names_known", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "hunt_lead", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('hub_sol_undertow:cc1') ? '' : 'unlocked-choice'">
<<link "Ask him about Doss and Rourke." "Canvas_hub_sol_undertow_Node_crew">><<script>>setup.markChoiceVisited("hub_sol_undertow:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Nothing tonight." "Location_underworld_bar">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<set _bp to random(0, 2)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Rule of the Undertow: down here nobody's who they were up there. Man walks in, whatever he did in the daylight stays at the door. Keeps the peace. Keeps my bar standing.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Bar's owned by somebody now who never comes down to look at it. Suits me. An owner you never see is an owner who never asks questions you can't answer.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> You want to last down here, you learn what I learned: hear it all, hold it all, hand out none of it for free. That's not being cold. That's the rent.</div></div>
<</if>><p>He talks the way a man talks who's decided you're all right — not warm, exactly, but no longer watching his own mouth. Down here that's most of the way to a friend.</p>
<<set $game_state.current_canvas = "hub_sol_undertow">>
<<set $game_state.current_node = "hub_sol_undertow.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_sol_undertow.talk") === -1) { vn.push("hub_sol_undertow.talk"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Let him work.->Location_underworld_bar]]<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I need a carrier. Somebody who moves freight both ways — outer world to under, under to outer — and never leaves it on paper. Company work, the kind that never happened.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Short list down here, and a shorter one that's any good. The one you want's called Colm. Runs sealed packets for a buyer who pays too well to name, and hasn't let a thing show up in a ledger in all the years I've watched him drink. Careful to a fault.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where do I find him.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Right here, most nights — corner stool, back to the wall, drinking slow so he stays sharp. In around seven, nurses it till close. Buy the man a drink. He warms slower than he'll ever admit, but he warms.</div></div><p>A name, a stool, a habit — the whole of it. She looks down the bar and finds him without trying: a wary regular three stools down, a bag with a boot hooked through the strap, eyes on the door out of pure reflex. Her thread up out of Calloway's file room and straight to whoever's buying.</p>
<<set $game_state.current_canvas = "hub_sol_undertow">>
<<set $game_state.current_node = "hub_sol_undertow.carrier">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_sol_undertow.carrier") === -1) { vn.push("hub_sol_undertow.carrier"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "colm_found", "set");<</script>>
[[Colm, then.->Location_underworld_bar]]<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Two men. Doss and Rourke. They came up out of the underworld before they ever ran for Renner. You'd have known them.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> Doss I knew. Ran cargo for the company back when it still pretended it didn't. Renner made an example of him — him and Rourke both, and it took. But Doss ran with a third. A fixer, name of Marsh. Careful little man, and still breathing.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/sol.jpg" alt="Sol" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Sol:</strong> He keeps one habit. The House, every Sunday — same girl, like clockwork. That's the only place Marsh surfaces. A man with one habit's got one weakness.</div></div><p>He sets the clean glass down. A name, a day, a door — the whole of it. Then, not looking up: "Whatever you carry that does your quiet work — carry it Sunday. Marsh doesn't talk twice."</p>
<<set $game_state.current_canvas = "hub_sol_undertow">>
<<set $game_state.current_node = "hub_sol_undertow.crew">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_sol_undertow.crew") === -1) { vn.push("hub_sol_undertow.crew"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "hunt_lead", "set");<</script>>
[[Sundays, then. Carry the drain.->Location_underworld_bar]]<p>The drain opens all the way and she takes him properly — not a skim, the whole live current of him coming loose through the contact. His Charge, his week, the last hour, going soft and white while she holds the pull steady and drinks it down.</p><p>And while he's this open, this undefended, she asks him the one thing she came for: where a company that never loses a page keeps what it can't afford to lose. He gives it up without knowing he's giving it — the deep vault, down in the docs department, the sealed archive nobody at her level ever badges into. Whatever Mercer planted her to fetch is sitting behind that door.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> So now she has the where — and a leash on the man who gave it to her. She can open him whenever the drain's hungry and he'll thank her for it; he'll never remember telling her a thing. Easier to keep him than to open that vault — and that part she'll have to solve herself.</div></div><p>He blinks, the minutes wiped, disoriented in his own chair. 'Sorry — lost the thread. Where were we.' She tells him it's nothing. Drink some water.</p>
<<set $game_state.current_canvas = "calloway_drain_canvas">>
<<set $game_state.current_node = "calloway_drain_canvas.d0">>
<<script>>setup.markCanvasTriggered("calloway_drain_canvas");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("calloway_drain_canvas.d0") === -1) { vn.push("calloway_drain_canvas.d0"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Straighten up.->Location_vance_securities]]<p>The drain fires the way it always does now — no drama, no discovery, just his Charge coming loose into her and the minutes going with it. He surfaces grateful and none the wiser, his hand finding hers like it's the only true thing in the room. She lets him think it is.</p>
<<set $game_state.current_canvas = "calloway_drain_canvas">>
<<set $game_state.current_node = "calloway_drain_canvas.done">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("calloway_drain_canvas.done") === -1) { vn.push("calloway_drain_canvas.done"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave him to it.->Location_vance_securities]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<p>He finishes on her face with a sound that's half relief, one hand cradling her head like she's the first good thing in two years. He'd give her anything now. He doesn't know what she's about to take.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_finish_facial_t5"] to ($game_state.media_cycle["sex_calloway_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_finish_facial_t5/c68f4bb2d43.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_finish_facial_t5/c244b433321.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_finish_facial_t5/c66861dfacf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_finish_facial_t5/cd0cc7eb194.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<p>He finishes inside her, holding her down on him, shaking, and after he just breathes into her neck — wrecked, grateful, believed. She lets him have it. Letting him have it is how she keeps him.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_finish_inside_t5"] to ($game_state.media_cycle["sex_calloway_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_finish_inside_t5/c10e9049798.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_finish_inside_t5/cc5df6e2942.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_finish_inside_t5/cc7cdae36aa.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_finish_inside_t5/ccae08a5789.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<p>He finishes in her ass, and the drain opens under it the way it's built to — his whole self coming loose through the contact, the last few minutes already going soft and white behind his eyes.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_finish_anal_t5"] to ($game_state.media_cycle["sex_calloway_finish_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_finish_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_finish_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_finish_anal_t5/c22e6b78ef7.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_finish_anal_t5/c79e5de0c9c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_finish_anal_t5/c037160e284.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_finish_anal_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>>
<<set $game_state.current_canvas = "loop_calloway_finisher">>
<<set $game_state.current_node = "loop_calloway_finisher.climax">>
<<script>>setup.markCanvasTriggered("loop_calloway_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_calloway_finisher.climax") === -1) { vn.push("loop_calloway_finisher.climax"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_finisher:cc0') ? '' : 'unlocked-choice'">
<<link "Straighten up." "Location_vance_securities">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_finisher:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_finisher:cc1') ? '' : 'unlocked-choice'">
<<link "Open the drain on him." "Canvas_calloway_drain_canvas_Node_d0">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "calloway_drains_done", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_finisher:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_finisher:cc2') ? '' : 'unlocked-choice'">
<<link "Take him again." "Canvas_calloway_drain_canvas_Node_done">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_finisher:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "gte", "value": 1}]})))>>
<<script>>
try {
window._engineNoExitsDiag_loop_calloway_finisher = [{"text": "Straighten up.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]}}, {"text": "Open the drain on him.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]}}, {"text": "Take him again.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "gte", "value": 1}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "loop_calloway_finisher",
choices: window._engineNoExitsDiag_loop_calloway_finisher,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_loop_calloway_finisher>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas loop_calloway_finisher</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Navigation]]
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_loop_t5"] to ($game_state.media_cycle["sex_calloway_loop_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_loop_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_loop_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_loop_t5/c3fc6d131a1.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_loop_t5/c8f262a391c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_loop_t5/ca6a0a04daa.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_loop_t5/cd18272086e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She shuts the file-room door for the last time as an analyst. He's already got his hands in her hair — the disbelieved man gone, just someone who's decided to trust the one person who ever let him.</p>
<<set $game_state.current_canvas = "loop_calloway_sex">>
<<set $game_state.current_node = "loop_calloway_sex.intro">>
<<script>>setup.markCanvasTriggered("loop_calloway_sex");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_calloway_sex.intro") === -1) { vn.push("loop_calloway_sex.intro"); }<</script>>
<<nobr>>
<<link "Get on your knees." "Canvas_loop_calloway_sex_Node_base_oral_c">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_loop_oral_t5"] to ($game_state.media_cycle["sex_calloway_loop_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_loop_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_loop_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_loop_oral_t5/c5284915299.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_loop_oral_t5/c9364663438.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_loop_oral_t5/cea1b77fc56.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_loop_oral_t5/cf29cbd1247.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees with his cock in her mouth, and even now he half-talks — the ghost, the case, the shape of the thing he's chased for two years — because the work is the only place he's ever felt safe, and she's made herself the second.</p>
<<set $game_state.current_canvas = "loop_calloway_sex">>
<<set $game_state.current_node = "loop_calloway_sex.base_oral_c">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_calloway_sex.base_oral_c") === -1) { vn.push("loop_calloway_sex.base_oral_c"); }<</script>>
<<nobr>>
<<link "Keep him in your mouth." "Canvas_loop_calloway_sex_Node_base_oral_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Climb on." "Canvas_loop_calloway_sex_Node_base_vaginal_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_sex:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_loop_calloway_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_sex:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_loop_vaginal_t5"] to ($game_state.media_cycle["sex_calloway_loop_vaginal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_loop_vaginal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_calloway_loop_vaginal_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_loop_vaginal_t5/c1e99326e8e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_loop_vaginal_t5/c17c708b624.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/calloway_loop_vaginal_t5/ce2766d3dab.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_loop_vaginal_t5/cfb743a44e0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She rides him in the desk chair, slow, and he holds her face in both hands like he's checking she's real. He tells her the true name of the rogue he's chasing. She files it. She files everything.</p>
<<set $game_state.current_canvas = "loop_calloway_sex">>
<<set $game_state.current_node = "loop_calloway_sex.base_vaginal_c">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_calloway_sex.base_vaginal_c") === -1) { vn.push("loop_calloway_sex.base_vaginal_c"); }<</script>>
<<nobr>>
<<link "Take him." "Canvas_loop_calloway_sex_Node_base_vaginal_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on him." "Canvas_loop_calloway_sex_Node_base_oral_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn around — give him your ass." "Canvas_loop_calloway_sex_Node_base_anal_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_sex:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_loop_calloway_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_sex:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_calloway_loop_anal_t5"] to ($game_state.media_cycle["sex_calloway_loop_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_calloway_loop_anal_t5"] + 1) % 3)>><<set _mc to $game_state.media_cycle["sex_calloway_loop_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/calloway_loop_anal_t5/c871b89f398.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/calloway_loop_anal_t5/c4123b1cad3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/calloway_loop_anal_t5/c908064aa8b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent over the desk he's guarded for two years, his cock in her ass, both his hands gripping like she's the last solid thing in a building that's been cutting him loose. This is the act that opens the drain — the one place her power still works freely, on a man who isn't the one who owns her.</p>
<<set $game_state.current_canvas = "loop_calloway_sex">>
<<set $game_state.current_node = "loop_calloway_sex.base_anal_c">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_calloway_sex.base_anal_c") === -1) { vn.push("loop_calloway_sex.base_anal_c"); }<</script>>
<<nobr>>
<<link "Take it in the ass." "Canvas_loop_calloway_sex_Node_base_anal_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back on him." "Canvas_loop_calloway_sex_Node_base_vaginal_c">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_calloway_sex:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish — and take what he knows." "Canvas_loop_calloway_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_calloway_sex:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><img src="./videos/scenes/calloway_flinch.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He stops her hand mid-reach and holds it — not gentle.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> Why you. Why now. Nobody in this building has been decent to me in two years and then you walk in and you're this. What do you actually want. Who sent you.</div></div><p>It's the closest he's come to the truth, and he doesn't even know it.</p><span id="cascade-b2-beat-1"><<linkreplace "Re-anchor him.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've been right for two years, and alone for two years. I'm not here to make you alone. I'm here because you're the only one who was ever right.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...God. I want that to be true.</div></div><p>He lets her hand go, and keeps it. The suspicion doesn't leave him. It just stops winning.</p>[[Let him have it.->Location_vance_securities]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_calloway_flinch">>
<<set $game_state.current_node = "cap_calloway_flinch.base">>
<<script>>setup.markCanvasTriggered("cap_calloway_flinch");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_calloway_flinch.base") === -1) { vn.push("cap_calloway_flinch.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "calloway_flinch_resolved", "set");<</script>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["scenes_rung_calloway_oral_t5"] to ($game_state.media_cycle["scenes_rung_calloway_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["scenes_rung_calloway_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["scenes_rung_calloway_oral_t5"]>><<if _mc is 0>><video src="./videos/scenes/rung_calloway_oral_t5/c0b7ba5d6ec.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/scenes/rung_calloway_oral_t5/c70f234c18c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/scenes/rung_calloway_oral_t5/c5289fb9d1d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/scenes/rung_calloway_oral_t5/ce3f958f196.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She takes him out from behind the desk and puts her mouth on him, and the sound he makes isn't lust — it's relief, like something clenched for two years finally letting go.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "lt", "value": 12}]})>>
<p>He tries to stay in his head — keeps talking, the case, the ghost, the names — even with his cock in her throat, because the work is the only place he's ever been safe. She lets him. She's good at making a man feel safe while she takes him apart.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "gte", "value": 12}]})>>
<p>Now he doesn't talk. He holds the back of her head, not pushing, just needing to know she's real, and she lets him fill her mouth and swallows it and calls it access.</p>
<</if>>
<<set $game_state.current_canvas = "rung_calloway_oral">>
<<set $game_state.current_node = "rung_calloway_oral.base">>
<<script>>setup.markCanvasTriggered("rung_calloway_oral");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_calloway_oral.base") === -1) { vn.push("rung_calloway_oral.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
[[Wipe your mouth. Go.->Location_vance_securities]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["scenes_rung_calloway_contact_t4"] to ($game_state.media_cycle["scenes_rung_calloway_contact_t4"] === undefined ? 0 : ($game_state.media_cycle["scenes_rung_calloway_contact_t4"] + 1) % 4)>><<set _mc to $game_state.media_cycle["scenes_rung_calloway_contact_t4"]>><<if _mc is 0>><video src="./videos/scenes/rung_calloway_contact_t4/c6d1c6dde0f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/scenes/rung_calloway_contact_t4/c8a588fb7d3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/scenes/rung_calloway_contact_t4/ce3dc2eca8b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/scenes/rung_calloway_contact_t4/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>The file room, late. He's stopped pretending to work; she's stopped pretending to file. When she puts a hand flat on his chest he goes still.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "lt", "value": 6}]})>>
<p>Not the still of a man being seduced — the still of a man who's forgotten what it is to be touched by someone who isn't about to take something from him. She is, of course. He just doesn't know it yet.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...What are you doing.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "gte", "value": 6}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> You don't have to do this part. Whatever he's paying you to get out of me — not this part.</div></div><p>He says it like a man hoping to be argued with. She doesn't argue. She just doesn't stop, and he lets her not stop.</p>
<</if>>
<<set $game_state.current_canvas = "rung_calloway_contact">>
<<set $game_state.current_node = "rung_calloway_contact.base">>
<<script>>setup.markCanvasTriggered("rung_calloway_contact");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_calloway_contact.base") === -1) { vn.push("rung_calloway_contact.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
[[Leave it there for tonight.->Location_vance_securities]]<p>With the sealed side of the hunt open to her now, she can finally see the far end — and the far end is a place Calloway has never once been.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> The pulls don't get sold up here. They go DOWN — into the Reach, through the kind of courier who never touches a ledger and never shows on a manifest. A company man could audit this room ten more years and never find him, because he isn't in any record there is. But she knows that world — and she knows who to ask. Down at the Undertow, Sol has poured for every carrier who ever worked the Reach; he'll know the man who runs this.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> I'll re-run the logistics side tomorrow. There's something in the courier contracts I keep missing — I can feel it.</div></div><p>She tells him that sounds right, and lets him keep chasing the ghost through paper. She already knows where she's going tonight: the Undertow, to put Sol's word to the man who carries it.</p>
<<set $game_state.current_canvas = "cap_case_pipe">>
<<set $game_state.current_node = "cap_case_pipe.base">>
<<script>>setup.markCanvasTriggered("cap_case_pipe");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_case_pipe.base") === -1) { vn.push("cap_case_pipe.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "pipe_known", "set");<</script>>
[[Head for the Reach.->Location_vance_securities]]<p>She stops chasing the leaks and starts reading the hunt itself — his two years of dead ends. And the dead ends have a pattern he's too close to see.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> The vendors, the couriers, the temp staff — I ran all of it. Ran my own people first, God help me, and they came back solid. Every thread just goes cold on me.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Every thread goes cold because the same hand keeps it cold. One of the few he trusts has been steering him off it for two years — vetting the lead that mattered, losing the record that pointed home. It's someone on his own team. She doesn't say it. The day she says it he's finished, and so is her way in.</div></div>
<<set $game_state.current_canvas = "cap_case_curated">>
<<set $game_state.current_node = "cap_case_curated.base">>
<<script>>setup.markCanvasTriggered("cap_case_curated");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_case_curated.base") === -1) { vn.push("cap_case_curated.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "case_curated_seen", "set");<</script>>
[[Say nothing. Keep going.->Location_vance_securities]]<p>A week of pulling boxes with him and the shape starts to show. She lays three of the leaked pulls side by side on his desk and taps them.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> No forced drawer. No missing page. Every one of these was clean, and every one was exactly worth taking. Nobody broke in, Calloway. Somebody walked it out the front door.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...An inside job. Two years I've said it and the audit calls me paranoid because there's never a mark. You put it on my desk in ten days.</div></div>
<<set $game_state.current_canvas = "cap_case_insider">>
<<set $game_state.current_node = "cap_case_insider.base">>
<<script>>setup.markCanvasTriggered("cap_case_insider");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_case_insider.base") === -1) { vn.push("cap_case_insider.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "case_insider_seen", "set");<</script>>
[[Keep digging.->Location_vance_securities]]<img src="./videos/scenes/calloway_access.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She's in the room enough now that he talks while he works — and tonight he stops working, and just looks at her like he's deciding something.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> Two years I've run this alone because nobody believed there was anything to run. You do. So I'm going to show you the part I don't show anyone — the pulls I never logged, the names I couldn't prove.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Show me all of it.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Take the keys.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...God, it's good to not be the only one anymore. Here — this is everything. Don't make me regret it.</div></div><p>He hands her the real access: the sealed side of the hunt, the run of it he's kept off every desk but his own. She's all the way in now — exactly where she needs to be.</p>[[Take it all.->Location_vance_securities]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_calloway_access">>
<<set $game_state.current_node = "cap_calloway_access.base">>
<<script>>setup.markCanvasTriggered("cap_calloway_access");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_calloway_access.base") === -1) { vn.push("cap_calloway_access.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "calloway_believes", "set");<</script>><p>A leaving card goes round in a manila envelope — third one this month. She signs under a dozen names she doesn't know, for a woman she's never met, packing a plant and a decade into one copier-paper box. Nobody's planning a party. There's nowhere left to have it.</p>
<<set $game_state.current_canvas = "amb_floor_goodbye">>
<<set $game_state.current_node = "amb_floor_goodbye.base">>
<<script>>setup.markCanvasTriggered("amb_floor_goodbye");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("amb_floor_goodbye.base") === -1) { vn.push("amb_floor_goodbye.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Pass it on.->Location_vance_securities]]<p>Two auditors in good suits are sealing the old records office with tamper tape and a printed inventory, working right past a name plate nobody's taken down. They don't look at her. To them the whole division is already a line item that closes Friday.</p>
<<set $game_state.current_canvas = "amb_floor_audit">>
<<set $game_state.current_node = "amb_floor_audit.base">>
<<script>>setup.markCanvasTriggered("amb_floor_audit");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("amb_floor_audit.base") === -1) { vn.push("amb_floor_audit.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Walk past.->Location_vance_securities]]<p>The lights over the far half of the floor have been off a week — nobody filed the ticket. A dead colleague's monitor still cycles a screensaver over a chair that isn't coming back, and the building keeps the heat on out of habit. It's very quiet in here for a place that's supposed to be working.</p>
<<set $game_state.current_canvas = "amb_floor_dying">>
<<set $game_state.current_node = "amb_floor_dying.base">>
<<script>>setup.markCanvasTriggered("amb_floor_dying");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("amb_floor_dying.base") === -1) { vn.push("amb_floor_dying.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Get back to it.->Location_vance_securities]]<p>She comes onto the floor out of the kit, back in her own sharp line, and Calloway catches it before she's three steps in. He knows her — that's not the problem. The problem is everyone else who knows a junior analyst doesn't dress like that.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> What are you doing up here looking like that. You want the audit asking who the analyst in the good coat is? Go. Change. Come back nobody.</div></div><p>He's right and they both know it. Nothing she needs off this floor happens while she's wearing the wrong face. The kit's back on the rack in her quarters.</p>
<<set $game_state.current_canvas = "react_calloway_nocover">>
<<set $game_state.current_node = "react_calloway_nocover.base">>
<<script>>setup.markCanvasTriggered("react_calloway_nocover");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("react_calloway_nocover.base") === -1) { vn.push("react_calloway_nocover.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Go change.->Location_vance_securities]]<img src="./videos/scenes/archive_cover.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She comes onto the Vance floor in her own face — the sharp company line, not the plain kit — and it lands wrong the second the light finds her. A junior analyst doesn't dress like she's worth more than the floor she's standing on. A head comes up. On a division being audited, a turned head is how a question starts.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Wrong skin for this floor. Nothing gets done looking like this. The kit's on the rack up in her quarters — where it does her no good.</div></div>
<<set $game_state.current_canvas = "react_calloway_precover">>
<<set $game_state.current_node = "react_calloway_precover.base">>
<<script>>setup.markCanvasTriggered("react_calloway_precover");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("react_calloway_precover.base") === -1) { vn.push("react_calloway_precover.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = setup.pendingEffects || [];setup.addToWardrobe("cover_analyst");setup.showEffectNotification();<</script>>
[[Back out before it's a question.->Location_spire_plaza]]<p>Vane's at the coffee machine, waiting out a slow drip, easy the way the other three never quite manage. He makes room for her without being asked.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> Don't let Reyes get to you. She's right that it's over and wrong about how to leave. You do the job clean to the last day — that's the one thing they can't take back. Calloway taught me that, funnily enough.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You believe him? The leak?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/vane.jpg" alt="Vane" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Vane:</strong> I believe there's always something moving that shouldn't be. Whether he ever gets his hands on it...</div></div><p>He shrugs, easy, and takes his coffee back to a desk tidier than anyone else's on a floor that's dying. The most trustworthy man in the room.</p>
<<set $game_state.current_canvas = "walkin_chat_vane">>
<<set $game_state.current_node = "walkin_chat_vane.base">>
<<script>>setup.markCanvasTriggered("walkin_chat_vane");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("walkin_chat_vane.base") === -1) { vn.push("walkin_chat_vane.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Let him get back to it.->Location_vance_securities]]<p>Marr's got the crossword folded to a quarter and a pen he isn't using. He raises it in a lazy salute.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/marr.jpg" alt="Marr" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Marr:</strong> The dream team. A woman who actually reads the files, a boss who believes in monsters, and me. You know the shredder's run more hours this month than the three of us combined? That's the real audit finding. Nobody writes it down.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You could help him.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/marr.jpg" alt="Marr" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Marr:</strong> I could. Six letters for 'why bother.'</div></div>
<<set $game_state.current_canvas = "walkin_chat_marr">>
<<set $game_state.current_node = "walkin_chat_marr.base">>
<<script>>setup.markCanvasTriggered("walkin_chat_marr");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("walkin_chat_marr.base") === -1) { vn.push("walkin_chat_marr.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Leave him his crossword.->Location_vance_securities]]<p>Reyes has her jacket on at her desk, phone face-up, one eye on the door out.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/reyes.jpg" alt="Reyes" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Reyes:</strong> Advice, since you're new and nobody else will: don't get pulled into the ghost story. There's no rogue. There's a sad man who missed his window and needs the leak to be real so the last two years meant something. I'm gone Friday. You should be looking too.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> And if the leak's real?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/reyes.jpg" alt="Reyes" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Reyes:</strong> Then it's above our clearance and someone else's problem. Same difference to us.</div></div>
<<set $game_state.current_canvas = "walkin_chat_reyes">>
<<set $game_state.current_node = "walkin_chat_reyes.base">>
<<script>>setup.markCanvasTriggered("walkin_chat_reyes");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("walkin_chat_reyes.base") === -1) { vn.push("walkin_chat_reyes.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Let her watch the door.->Location_vance_securities]]<p>Voss is taping a box shut at the desk he's had since before she was built. He doesn't stop for her.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/voss.jpg" alt="Voss" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Voss:</strong> Thirty-one years. They give you a badge and a mug and tell you it means something, and one Tuesday a kid with a clipboard decides the whole floor's a rounding error. Calloway's the only one who ever gave a damn what was in these boxes.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> He still does.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/voss.jpg" alt="Voss" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Voss:</strong> That's the tragedy of him, isn't it.</div></div>
<<set $game_state.current_canvas = "walkin_chat_voss">>
<<set $game_state.current_node = "walkin_chat_voss.base">>
<<script>>setup.markCanvasTriggered("walkin_chat_voss");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("walkin_chat_voss.base") === -1) { vn.push("walkin_chat_voss.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Leave him to his boxes.->Location_vance_securities]]<<set _sub_target = setup.checkAndSubstituteCanvas("chat_the_floor")>><<if _sub_target>><<goto _sub_target>><</if>>
<p>She takes a lap of the floor with a coffee she won't drink — the analyst's oldest cover, looking busy going nowhere. Half the desks are boxed up. The other half pretend the audit isn't three feet away.</p>
<<set $game_state.current_canvas = "chat_the_floor">>
<<set $game_state.current_node = "chat_the_floor.base">>
<<script>>setup.markCanvasTriggered("chat_the_floor");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("chat_the_floor.base") === -1) { vn.push("chat_the_floor.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Back to it.->Location_vance_securities]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})>>
<p>She pulls a box he hasn't reached and starts cross-referencing the dates against the pulls he's flagged — the actual work, the part every other minder skipped straight past on the way to the close order.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> You're... actually reading them. Huh. Left column's the ones that went missing clean. Right's the ones somebody dressed up after. Don't mix them or I lose a week.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then I won't mix them.</div></div><p>He goes quiet and works beside her — not warming to her, exactly. Warming to not being alone in it.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "gte", "value": 1}]})>>
<p>She works the boxes beside him the same as before — except it's cover now, not courtship. He believes every inch of her; she's the one who decides what the case turns up, and when.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> Left column clean, right column dressed. You know the drill by now. Don't know what I did before you.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Nothing. He did nothing before her and he'll remember nothing after. She logs another hour at the desk of a man who's already hers.</div></div>
<</if>><p>She squares the pulled boxes back into the row and marks the hour on his sheet.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "case_insider_seen", "operator": "is_false"}]})>>
<p>Dates, pull-slips, signatures. The boxes stay boxes. Whatever's in here is in the pattern and not the pages — and the pattern needs more of them open than she's got open.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "case_insider_seen", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "case_curated_seen", "operator": "is_false"}]})>>
<p>She has the how now: nobody broke in. What she doesn't have is whose hand. That's in his two years of dead ends, not in the thefts — and there are a lot of dead ends left to read.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "case_curated_seen", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "calloway_believes", "operator": "is_false"}]})>>
<p>The trail runs into the part of the hunt he keeps sealed, and no box on this floor opens that. He does — when he decides she's worth handing it to.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "case_curated_seen", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "calloway_believes", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "pipe_known", "operator": "is_false"}]})>>
<p>The sealed side is hers now and the far end is close. A pass or two more through the logistics — the contracts, the manifests that don't match — and she'll have where it goes.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "pipe_known", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "lt", "value": 1}]})>>
<p>The paper's finished. It gave up the how and it gave up where, and it will never give up a name — the man carrying it was never written down. That answer's down in the underworld, at the Undertow, not in this room.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "colm_drains_done", "operator": "gte", "value": 1}]})>>
<p>She has the rogue, and he's two desks away. Nothing in these boxes confirmed it and nothing in them ever will. What's left isn't filing. It's Vane.</p>
<</if>>
<<set $game_state.current_canvas = "work_the_case">>
<<set $game_state.current_node = "work_the_case.base">>
<<script>>setup.markCanvasTriggered("work_the_case");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("work_the_case.base") === -1) { vn.push("work_the_case.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_calloway", "relation", "add", 3.0, false, null);setup.applyAndNotifyTrait("player", null, "case_progress", "add", 1.0, false, null);setup.showEffectNotification();<</script>>
[[Log the hour.->Location_vance_securities]]<<set _sub_target = setup.checkAndSubstituteCanvas("hub_calloway_fileroom")>><<if _sub_target>><<goto _sub_target>><</if>>
<img src="./videos/scenes/calloway_fileroom.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Calloway's file room. Boxes to the ceiling, an index on none of them, a man who knows where everything is and trusts no one else to touch it. He works like the audit isn't three streets away with a shredder.</p>
<<set $game_state.current_canvas = "hub_calloway_fileroom">>
<<set $game_state.current_node = "hub_calloway_fileroom.base">>
<<script>>setup.markCanvasTriggered("hub_calloway_fileroom");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_calloway_fileroom.base") === -1) { vn.push("hub_calloway_fileroom.base"); }<</script>>
<<nobr>>
<<link "Talk to him." "Canvas_hub_calloway_fileroom_Node_talk">><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "calloway_believes", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "gte", "value": 0}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('hub_calloway_fileroom:cc0') ? '' : 'unlocked-choice'">
<<link "Get close to him." "Canvas_rung_calloway_contact_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_calloway", "corruption", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_calloway", "arousal", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_calloway_fileroom:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Get close to him.">Get close to him. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "calloway_believes", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "gte", "value": 6}, {"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('hub_calloway_fileroom:cc1') ? '' : 'unlocked-choice'">
<<link "Take him in your mouth." "Canvas_rung_calloway_oral_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_calloway", "corruption", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_calloway", "arousal", "add", 4.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "calloway_oral_once", "set");<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_calloway_fileroom:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Take him in your mouth.">Take him in your mouth. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "calloway_believes", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "calloway_flinch_resolved", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_calloway", "trait_key": "corruption", "operator": "gte", "value": 18}]})>>
<span @class="setup.isChoiceVisited('hub_calloway_fileroom:cc2') ? '' : 'unlocked-choice'">
<<link "Take him — all the way." "Canvas_loop_calloway_sex_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "calloway_fucked_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_calloway_fileroom:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Not yet — he has to trust you all the way down first.">Not yet — he has to trust you all the way down first.</span><br>
<</if>>
<<link "Leave him to the paper." "Location_vance_securities">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "lt", "value": 1}]})>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Walk me through the ghost. From the start — the parts nobody let you finish.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...Fine. You keep up, and you keep it in this room. I've had enough people carry half of it out and call me paranoid.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "calloway_drains_done", "operator": "gte", "value": 1}]})>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Anything new on the ghost?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> For you? Everything I've got. Don't know what I ran this on before you walked in.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> He hands it over before she's done asking. He'll never know the file room's been hers since the night his own current gave up where the vault is.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_calloway_fileroom">>
<<set $game_state.current_node = "hub_calloway_fileroom.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_calloway_fileroom.talk") === -1) { vn.push("hub_calloway_fileroom.talk"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_calloway", "relation", "add", 2.0, false, null);setup.showEffectNotification();<</script>>
[[Keep at it.->Location_vance_securities]]<img src="./videos/scenes/calloway_meet.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He's at the file wall when she comes in, and he doesn't turn — just clocks the badge in the dark glass and files her under the same heading as everyone else Mercer's sent down here.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> Nobody reads the file. They read the summary, sign the close order, and go to lunch. That what you're here to do — sign it?</div></div><p>He says it the way he's said it a hundred times, to a hundred minders.</p><span id="cascade-b2-beat-1"><<linkreplace "Take the hunt seriously.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The rogue's real. You're the only one still looking. That's the file I'd want to read.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/calloway.jpg" alt="Calloway" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Calloway:</strong> ...Huh. They usually don't bother learning the words. Sit down. Don't touch anything.</div></div>[[Get to work.->Location_vance_securities]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_calloway_meet">>
<<set $game_state.current_node = "cap_calloway_meet.base">>
<<script>>setup.markCanvasTriggered("cap_calloway_meet");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_calloway_meet.base") === -1) { vn.push("cap_calloway_meet.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "calloway_met", "set");<</script>><img src="./videos/scenes/archive_placement.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The badge desk scans her lanyard without looking up and waves her at an empty chair — one of a dozen, most already boxed. She's on Calloway's division now, or what the audit's left of it.</p><p>The woman at the next desk clocks the new badge and doesn't bother pretending she's working.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/reyes.jpg" alt="Reyes" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Reyes:</strong> Nobody told you what you walked into. Fine — since I'm the only one who's going to.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/reyes.jpg" alt="Reyes" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Reyes:</strong> Voss over there's done thirty years and still thinks Calloway hung the moon. Marr does the crossword — that's the whole job now. Vane you'll barely notice, keeps his head down, does it clean. And me, I'm gone the second something better calls back.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/reyes.jpg" alt="Reyes" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Reyes:</strong> Advice? Answer the auditors, not him. Calloway's chasing a ghost, and half of us think the ghost is just him coming apart. Don't catch it.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Four people running out the clock on a dead floor. To them she's one more temp badge, gone before the boxes are. Exactly what the cover needs — no one worth remembering.</div></div>
<<set $game_state.current_canvas = "cap_archive_placement">>
<<set $game_state.current_node = "cap_archive_placement.base">>
<<script>>setup.markCanvasTriggered("cap_archive_placement");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_archive_placement.base") === -1) { vn.push("cap_archive_placement.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "floor_placed", "set");<</script>>
[[Find the file room.->Location_vance_securities]]<img src="./videos/scenes/archive_cover.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The plain kit holds all the way up — the junior-analyst badge Mercer bought clean, the grays that make her no one worth a second look, a desk in a division being quietly shut down. The lift opens on Vance Securities: half the lights off, half the desks empty, the audit already eating it from inside.</p><p>The file room is Calloway's. He runs the company's hunt for the rogue out of it, and the floor treats him like a crank chasing a ghost. The paper he keeps has no index — the only map of it lives in his head.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> A plain company errand, the way Mercer put it. Get in, take the room, bring it back. She doesn't turn over what might sit under a job this simple. First she needs the man who keeps it.</div></div>
<<set $game_state.current_canvas = "cap_archive_cover">>
<<set $game_state.current_node = "cap_archive_cover.base">>
<<script>>setup.markCanvasTriggered("cap_archive_cover");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_archive_cover.base") === -1) { vn.push("cap_archive_cover.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "archive_started", "set");<</script>>
[[Get to work.->Location_vance_securities]]<img src="./videos/scenes/salvage_relaunch.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She comes up the ramp into the grey morning, and the comm in her jaw clicks live before she's ten steps clear — Mercer, who always knows the minute she's operational again.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You went dark. Days. I don't like dark, and I like even less what walks back out of it. Who put hands on you.</div></div><span id="cascade-b3-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> A mechanic. Off the books. I'm operational.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> A mechanic. You let some dockside butcher open you up and go rooting around in company property. You have any idea what you're worth. What's in you.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> More than you'll ever say out loud. I've met the part you keep the key to.</div></div><span id="cascade-b3-beat-1"><<linkreplace "Take the order.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> New target. Calloway — Vance Securities, internal division. He runs the hunt for the rogue from the inside, and he keeps a file room nobody audits. Get in. Bring me what's in it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There's a junior-analyst kit in your quarters. Wear it onto that floor — badge, plain grays, the whole nothing look. Not your own face. A division under audit remembers anyone who looks worth remembering.</div></div><span id="cascade-b3-beat-2"><<linkreplace "Say yes.">><p>She says yes the way she says yes to everything. But it sits on a new floor now. Every order he gives her, he gives holding the one key that can cut the thing at the root of her — and he's never once had to say so, because until three days ago she didn't know it was there. Now she does. It doesn't change the yes. It changes what she's saying yes for.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You still in one piece?</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Operational.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Then move. And Wren — next time you break, you break to me. Not to some yard rat. What's mine gets fixed by mine. Understand.</div></div>[[Go to work.->Location_the_waterfront]]
<</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "salvage_relaunch">>
<<set $game_state.current_node = "salvage_relaunch.base">>
<<script>>setup.markCanvasTriggered("salvage_relaunch");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_relaunch.base") === -1) { vn.push("salvage_relaunch.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "salvage_relaunched", "set");<</script>>
<<script>>setup.pendingEffects = setup.pendingEffects || [];setup.addToWardrobe("cover_analyst");setup.showEffectNotification();<</script>><p>The lift opens on the penthouse and Mercer looks up from the glass wall — and stops. Whatever he keeps her for goes out of his face and something colder comes in. He crosses the floor and takes her jaw in one hand, turning her face to the light like a man checking a coin for a file-mark.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> What did they do to you.</div></div><span id="cascade-b2-beat-0"><p>She doesn't answer. There isn't an answer that helps her. He turns her head the other way, reading the wrongness off her the way she reads it off a man — except he had the building of her, and he knows the manual.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You go dark for days, you come back moving like that, the core reading wrong through your skin. No. An asset that limps is a liability, and I don't hand the Chairman a liability.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Hear the refusal.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You get nothing from me like this. No target, no work, nothing — I'm not spending you broken. Go get yourself right, then come back and work. Not before.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Now get out of my sight.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Go.">><p>The lift takes her back down. She has her answer, and it's the one the dock already gave her: there's no working her way back in until she can fire. Which means the berth. Which means coin. Which means down the gate, where coin gets made.</p>[[Back down.->Location_the_waterfront]]
<</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "salvage_mercer_shocked">>
<<set $game_state.current_node = "salvage_mercer_shocked.base">>
<<script>>setup.markCanvasTriggered("salvage_mercer_shocked");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_mercer_shocked.base") === -1) { vn.push("salvage_mercer_shocked.base"); }<</script>>
<<script>>advanceTime(3);<</script>><img src="./videos/scenes/kess_extraction.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The lamp is up, not down. He wants to see.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Sit. No — lie down. I've had the tools out since you told me it fired.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I can take it now. Couldn't before, and I want you to know the difference before I open you.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Ask the difference.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What changed.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Your build's been talking to it. While that thing's answering in his voice the guard's asleep, and a sleeping guard I can cut around.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Blind, it takes you with it. I said that and I meant it.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Get on the bench.">><p>She lies face-down on the steel, bare from the hip. The fourth part is still in her and the key is still beside it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Arms flat. Last time you're on this bench for this, and I'd rather it was quick than pretty.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Hold still.">><p>It takes longer than the parts did. He works under the core rather than in it, and twice he stops and does nothing at all for a while.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't talk. If it wakes I want to hear it before you feel it.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Wait.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Two years that's been sat in you, and it's not coming out because I asked it nicely. Talk to me about something else.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Like what.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Anything. People bleed less when they're annoyed.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Wait it out.">><p>Then a change in the pressure — something coming free that was never meant to come free — and the quality of the room changes with it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. That's out.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't look at me like that. It came out clean, and that's the last surprise you get tonight.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Sit up.">><p>He puts it on the steel in front of her. It is smaller than a coin and it does not look like anything.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Eleven years of somebody's paperwork, and that's the whole of what they put in you to make it stick.</div></div><span id="cascade-b2-beat-7"><<linkreplace "Look at it.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It's small.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> They always are. The expensive part isn't the thing, it's the years of arguing about what it ought to do.</div></div><p>She does not touch it. Two years of her life is on the bench and it is a grey speck.</p><span id="cascade-b2-beat-8"><<linkreplace "The file.">><p>He wipes his hands, pulls the file over, and sets the chip down on the sealed page — a man matching a key to a lock, because that is what he is doing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Hardware's honest. Whoever built one built the other.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Same hand. I told you. Watch.</div></div><span id="cascade-b2-beat-9"><<linkreplace "Watch.">><p>The seal on the page reads the thing that came out of her, decides it is allowed, and lets go.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There's your page. I've not looked. I'll be over there.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And don't thank me for it. You paid for every hour.</div></div><p>He goes over there.</p><span id="cascade-b2-beat-10"><<linkreplace "Read it.">><p>Her line is on the index where it has always been, and the designation on it is older than she is by four years.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> They wrote me down four years before there was anything to write down.</div></div><span id="cascade-b2-beat-11"><<linkreplace "Keep reading.">><p>Under it, the word she got three pages into a file in this room and could not read past: BUILD.</p><p>Above her line and below it, the others. The index does not start at one.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> There's an office in this. People. Eleven years of somebody coming to work.</div></div><span id="cascade-b2-beat-12"><<linkreplace "The columns.">><p>Two date columns, the way Kess read them off it. The first is filled all the way down. The second is filled most of the way down.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The second one's when they stopped.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Aye.</div></div><span id="cascade-b2-beat-13"><<linkreplace "Find your own.">><p>Hers is blank. She goes up the column and back down it, counting the ones that aren't, and stops counting.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Most of them are dead.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Most of them are stopped. That's the word they used and I'd not go past it.</div></div><span id="cascade-b2-beat-14"><<linkreplace "The last line.">><p>The programme closes on the page before hers — sign-offs, a date, an office winding up work it had finished.</p><p>Her page was sealed eight months after that, by a hand on none of the sign-offs. Somebody outside came back to a closed thing and shut one page.</p><span id="cascade-b2-beat-15"><<linkreplace "Put it down.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Mercer sent her up to a file room to steal a company's paperwork and never once looked at what he was sending. He put her on it himself. Two years of errands, and the last one was to go and fetch herself.</div></div><span id="cascade-b2-beat-16"><<linkreplace "Say something to him.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Kess.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I'd rather not know. You'll tell me if you need a thing built and I'll price it, and the rest of it's yours.</div></div><p>That is as close to kindness as the Reach comes, and she takes it as such.</p><p>She puts the file under her arm and stands up off the bench with nothing in her that answers to anybody.</p><p>Somewhere out there is a man who came through a door for her and would not say why he cared. He does not have a name yet.</p>[[Take it and go.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_extraction">>
<<set $game_state.current_node = "cap_extraction.base">>
<<script>>setup.markCanvasTriggered("cap_extraction");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_extraction.base") === -1) { vn.push("cap_extraction.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "leash_cut", "set");setup.applyAndNotifyFlag("player", null, "file_read", "set");<</script>><p>He is still in her ass and still down on her back, spent and heavy and going nowhere. She reaches down inside for the old pull, the thing that opens a man at the base of the spine, and on him there is nothing to catch on.</p><span id="cascade-b1-beat-0"><p>Cold socket, dead as before. The same nothing as every time he has finished in her, and the exact shape of the last two years.</p><p>She stays the twenty minutes he wants, because leaving early would be a change in her, and there is nothing in this room she can afford to change.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come whenever. You know where the door is.</div></div></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.cold">>
<<script>>setup.markCanvasTriggered("mercer_drain_canvas");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.cold") === -1) { vn.push("mercer_drain_canvas.cold"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Get your coat.->Location_mercer_room]]<p>His cock is still in her ass, softening, his weight settled forward on her back. She reaches for it at the finish, the same reach she made on Kess's cradle.</p><span id="cascade-b1-beat-0"><p>Something small at the base of her spine says its piece into an empty room. Nothing hears it. Nothing pushes back.</p><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>She waits, because waiting is the only part of this she was ever given to do. Ten seconds. Twenty.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You've gone quiet on me. You always did go quiet at the end.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Nothing.">><p>It is the same nothing as every time he has finished in her. No give. No opening. The exact shape of the last two years.</p><span id="cascade-b1-beat-3"><<linkreplace "Let him have his hour.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> That's better. That is genuinely better. You want a drink? No. No, of course. I'll have yours.</div></div><p>He does up his belt and pours, and he is happier than she has seen him since the floor.</p><span id="cascade-b1-beat-4"><<linkreplace "Hold still.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> She had not expected anything and she is disappointed anyway, which is new. The part is dead in her, cooked the way he said it would cook. Somebody paid twenty-five coin for that.</div></div><p>She stays the twenty minutes he wants, because leaving early would be a change in her, and there is nothing in this room she can afford to change.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come whenever. You know where the door is.</div></div>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.try1">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.try1") === -1) { vn.push("mercer_drain_canvas.try1"); }<</script>>
<<script>>advanceTime(30);<</script>><p>He is still buried in her ass, his belly warm against her back, his cum in her. She reaches for it at the finish. This time something reaches back.</p><span id="cascade-b1-beat-0"><p>Not the part. The other thing, the older one, the one Kess found under her core and did not like.</p><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><p>It closes on her the way a hand closes. Everything below the hips goes rigid and stays rigid, and none of it is hers.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Christ. There. There — hold that, don't you dare move.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Don't make a sound.">><p>What is trying to get out of her is not a sound he has ever heard her make, and she holds it behind her teeth.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two years and you've never once done that. I'd have said you couldn't. I'd have been wrong.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Let him take it for what he wants.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> The floor. The glass. The woman with the plants. I'd give the lot of it back for a Thursday that went like this one is going.</div></div><p>He settles in behind her, pleased, in no rush to be anywhere at all.</p><span id="cascade-b1-beat-4"><<linkreplace "Count.">><p>She counts. At eleven her hand has closed on the bed frame hard enough to shake it, and she opens it finger by finger.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You're shaking. That's new as well. Everything about tonight is new and I've decided not to ask why. Are you cold?</div></div><span id="cascade-b1-beat-5"><<linkreplace "Give him something.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It's cold in here.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> It is. I keep meaning to do something about that and I never do. There's a stove two rows over I could have for nothing.</div></div><p>He does not look at her face. He has not looked at her face in two years.</p><span id="cascade-b1-beat-6"><<linkreplace "Wait it out.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> It did nothing at all last time. Tonight it did something, and what it did was hold her down. She cannot tell whether that counts as progress.</div></div><p>It lets go all at once, the way a cramp lets go, and leaves her legs useless for a minute. She waits until they work.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Thursday, then. I've started liking having a Thursday. Don't tell anyone.</div></div>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.try2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.try2") === -1) { vn.push("mercer_drain_canvas.try2"); }<</script>>
<<script>>advanceTime(30);<</script>><p>His cock is still in her ass, heavy, his breath going out slow against her shoulder. She reaches for it at the finish. This time nothing stops her.</p><span id="cascade-b1-beat-0"><p>Something that has been shut since before she can remember is not shut. No warning. It is simply not there.</p><span id="cascade-b1-beat-1"><<linkreplace "Reach.">><p>She reaches down inside for the old pull, the thing that opens a man at the base of the spine, and on him it catches.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Stay there a minute. I've got nowhere to be and you've got nowhere better, and I've decided that's what a Thursday is.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Hold it.">><p>Not secrets. The beginning of one — a man belonging to her, the width of a hair, and hers.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There's a stove going two rows over for nothing. I keep not buying it. I'd rather have the coin in a bottle and the cold to complain about.</div></div><span id="cascade-b1-beat-3"><<linkreplace "Then it counts.">><p>Then something goes down a list. An item. An item. A gap — and on the gap it comes back and shuts the door.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Nine years I had a man to do that sort of thing. Never learned his name either. You'd think I'd have learned one of them.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Check it.">><p>She reaches again while he is still inside her. Nothing catches. Cold socket, dead as before.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Don't move yet. That's the whole of what I've got left, that bit, and I'd like it to run long.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Wait it out.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Two years of nothing was survivable while it was a wall. It is a door. It opened, and something on the other side counted her and shut it.</div></div><p>He does up his belt one-handed and goes for the glass, pleased in the ordinary way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Best part of it now is nobody signed for it. You come because you come, and that's worth more than the floor was.</div></div><p>He has no idea he has said anything.</p>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.try3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.try3") === -1) { vn.push("mercer_drain_canvas.try3"); }<</script>>
<<script>>advanceTime(30);<</script>><p>His cock is still in her ass and his whole weight is on her back, and he has not moved since he came. She reaches down inside for the old pull, the thing that used to open a man at the base of the spine.</p><span id="cascade-b1-beat-0"><p>It catches. She waits for the list — for the item, and the item, and the gap where she is that always closes. Nothing comes down. Nothing comes at all.</p><p>The room stays exactly as quiet as a room.</p><span id="cascade-b1-beat-1"><<linkreplace "Take it.">><video src="./videos/sex/mercer_lockup_anal_drain_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Then the whole of him comes loose — not a skim, the live current of him going soft and honest through her while he lies on her back with his cock still in her and lets it happen.</p><span id="cascade-b1-beat-2"><<linkreplace "Ask him.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Who signed for me.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Halloran. Asset Services, fourth floor. Never met the man. A signature on a transfer and a number I never called.</div></div><p>His voice comes out even. He answers the way a man answers in his sleep.</p><span id="cascade-b1-beat-3"><<linkreplace "And what was he told.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What was Halloran told I was.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Sensitive recovery. One of theirs, on loan, keep her off the manifests. That's the whole of it. Four lines and a signature.</div></div><span id="cascade-b1-beat-4"><<linkreplace "And you.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> And what were you told.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Less. She's yours, she does what you say, don't break her, don't ask. I didn't ask. It never once occurred to me to ask.</div></div><p>There is no shame in it. There is nothing in it at all.</p><span id="cascade-b1-beat-5"><<linkreplace "The key.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The key in your coat. What is it for.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Christ knows. They handed it over with you. Said if she stops working, this is who you call — so I took it for the off-switch.</div></div><span id="cascade-b1-beat-6"><<linkreplace "Two years.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two years in a coat pocket. I thought it was there so you couldn't turn on me. A perk, not a job. I never once asked what it did.</div></div><p>He carried the thing that owns her as a courtesy, and never had the faintest idea what it was for.</p><span id="cascade-b1-beat-7"><<linkreplace "Then say the rest.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then how does it know you.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> It doesn't know me. Mine only speaks for me — I remember, because it annoyed me. Mine. Like there were others out there with one.</div></div><span id="cascade-b1-beat-8"><<linkreplace "Ask for the rest of them.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Who else.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> How would I know. I got a key and a girl and a floor to lose. I wasn't senior. I was the one who signed.</div></div><p>Nothing in him goes any further. He is the bottom of it and he was never anything else.</p><span id="cascade-b1-beat-9"><<linkreplace "Let go.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Two years owned by a man who was handed her with four lines and a signature. The leash is off for him and on for everyone else on that list, and she has no idea how long the list is.</div></div><span id="cascade-b1-beat-10"><<linkreplace "Let him come back.">><p>He surfaces ten minutes later on his own bed, out of her at last, blinking, the good mood of a man who has had a nice evening still on him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Where'd you go. Sit down and have one. There's a whole bottle of it and it came in a box.</div></div><p>She goes out past the shuttered stall and up into the market carrying, for the first time since the floor, something that is not on loan.</p><p>Behind her he is pouring, and none the wiser, and pleased.</p>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.d0">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.d0") === -1) { vn.push("mercer_drain_canvas.d0"); }<</script>>
<<script>>advanceTime(3);<</script>><p>He is still in her ass with his weight down on her, exactly where he has been for three of these. She reaches down inside for the old pull, the way she has three times now.</p><span id="cascade-b1-beat-0"><p>It catches. She holds still and waits to be shut, and the list comes down the way it has always come down. An item. An item. And then the gap where she is, and the thing that lives in that gap turning over to look at her.</p><span id="cascade-b1-beat-1"><<linkreplace "Hold it.">><p>Something at the base of her spine answers it in his voice. Two words, maybe three. She never hears them.</p><p>The list moves on past her and the door stays open.</p><span id="cascade-b1-beat-2"><<linkreplace "Take it.">><video src="./videos/sex/mercer_lockup_anal_answered_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Then the whole of him comes loose, the same as the first time, the live current of him going soft and honest through her while he lies on her back with his cock still in her ass.</p><span id="cascade-b1-beat-3"><<linkreplace "Hold it open.">><p>She holds it open longer than she needs to, the way you press a bruise to check. Nothing comes back for it. The key is awake on Kess's bench and it has nothing left to say about her.</p><span id="cascade-b1-beat-4"><<linkreplace "Ask him something.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Do you feel different.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Different. No. Should I? I feel like a man who's had a nice evening, which is more than I've had in a month.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Let go.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Every door that has opened for her down here was somebody else's — his bench, that hand, a key on a cloth. The thing that spoke for her just now is in her, and nobody gave it to her.</div></div><span id="cascade-b1-beat-6"><<linkreplace "Let him come back.">><p>He surfaces ten minutes later on his own bed, out of her at last, blinking, the good mood of a man who has had a nice evening still on him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Where'd you go. Sit down and have one. That chair's oak. I keep telling you and you keep not sitting in it.</div></div><p>She goes out past the shuttered stall and up into the market carrying, for the first time since the floor, something that is not on loan.</p><p>Behind her he is pouring, and none the wiser, and pleased.</p>[[Out through the stall.->Location_underworld_market]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.d1">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.d1") === -1) { vn.push("mercer_drain_canvas.d1"); }<</script>>
<<script>>advanceTime(3);<</script>><p>Same room, same bottle, his cum still in her ass and his hand still flat on her back.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Thursday. I've stopped pretending I don't count them. A man my age gets one thing to look forward to and stops being clever about it.</div></div><p>He gets what he always got.</p><span id="cascade-b1-beat-1"><<linkreplace "Take it.">><p>The drain fires the way it fires now — no discovery in it, just her owner coming loose into her and the minutes going with him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Don't rush off. That's the whole of what I've got left, that bit, and I'd like it to run long.</div></div><span id="cascade-b1-beat-2"><<linkreplace "Wait it out.">><p>He says it every time, and every time he has just said it for the first time. There is nothing left in him she needs.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You'll come round again. You always do. I'd not have bet on that a year ago and I'd have been wrong.</div></div><p>He surfaces grateful, pours himself one, and asks if she's stopping. She is not. She never is.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Thursday, then. I'll get a second glass in. A man ought to have two glasses.</div></div>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "mercer_drain_canvas">>
<<set $game_state.current_node = "mercer_drain_canvas.done">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_drain_canvas.done") === -1) { vn.push("mercer_drain_canvas.done"); }<</script>>
<<script>>advanceTime(30);<</script>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_finish_facial_t5"] to ($game_state.media_cycle["sex_mercer_lockup_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_finish_facial_t5/c47fe8c4b56.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_finish_facial_t5/c07381d25dc.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_finish_facial_t5/c34492a437d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_finish_facial_t5/c948373234d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes on her face, one hand on himself and one in her hair, and looks down at what he has put there with plain satisfaction. Nothing comes loose but him. The drain stays shut — it only opens the one way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There. Don't take it on the sleeve, there's a cloth on the crate. I'm not an animal about it and I never was.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_finish_inside_t5"] to ($game_state.media_cycle["sex_mercer_lockup_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_finish_inside_t5/c0e36cfc5c3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_finish_inside_t5/c4d4df0b08a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_finish_inside_t5/c2441a33809.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_finish_inside_t5/cf13f8c9ffd.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes in her cunt without hurrying that either, holds there a moment, and does up his belt one-handed. His cum runs on the inside of her thigh and she leaves it. She has taken nothing. Not this way.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come Thursday. Or don't. You know where the door is.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_anal_finish_t5"] to ($game_state.media_cycle["sex_mercer_lockup_anal_finish_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_anal_finish_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_anal_finish_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_anal_finish_t5/cb9b8d08334.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_anal_finish_t5/cbc9427b847.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_anal_finish_t5/cc63b029e44.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_anal_finish_t5/cd7f779eaf8.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes in her ass without changing pace and holds there, one hand still flat on her back, breathing out like a man sitting down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Ah. Stay there a moment. Don't go anywhere yet.</div></div>
<</if>>
<<set $game_state.current_canvas = "loop_mercer_finisher">>
<<set $game_state.current_node = "loop_mercer_finisher.climax">>
<<script>>setup.markCanvasTriggered("loop_mercer_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_mercer_finisher.climax") === -1) { vn.push("loop_mercer_finisher.climax"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc0') ? '' : 'unlocked-choice'">
<<link "Get your coat." "Location_mercer_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc0");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc1') ? '' : 'unlocked-choice'">
<<link "Let it go — wrong gear." "Location_mercer_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc1");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc2') ? '' : 'unlocked-choice'">
<<link "Let it go — the drain's empty. Take a night on the feed line at the berth and it reloads." "Location_mercer_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc2");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc3') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_cold">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc3");<</script>><<script>>advanceTime(30);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc4') ? '' : 'unlocked-choice'">
<<link "Reach for it at the finish." "Canvas_mercer_drain_canvas_Node_d0">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_drains_done", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_leash_state", "set", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "owner_drained", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc4");<</script>><<script>>advanceTime(90);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc5') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_cold">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc5");<</script>><<script>>advanceTime(30);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc6') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_cold">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc6");<</script>><<script>>advanceTime(30);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc7') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_try1">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "part_installed", "set", 0.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_attempts", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "spec_pending", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc7");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc8') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_try2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "part_installed", "set", 0.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_attempts", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "spec_pending", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc8");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 3}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc9') ? '' : 'unlocked-choice'">
<<link "Reach for it." "Canvas_mercer_drain_canvas_Node_try3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "part_installed", "set", 0.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_attempts", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "spec_pending", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc9");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc10') ? '' : 'unlocked-choice'">
<<link "Reach for it at the finish." "Canvas_mercer_drain_canvas_Node_d1">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_drains_done", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_leash_state", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "leash_answered", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc10");<</script>><<script>>advanceTime(90);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_finisher:cc11') ? '' : 'unlocked-choice'">
<<link "Take it." "Canvas_mercer_drain_canvas_Node_done">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "mercer_drains_done", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_finisher:cc11");<</script>><<script>>advanceTime(60);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_false"}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_false"}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 3}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 2}]})))>>
<<script>>
try {
window._engineNoExitsDiag_loop_mercer_finisher = [{"text": "Get your coat.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]}}, {"text": "Let it go \u2014 wrong gear.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]}}, {"text": "Let it go \u2014 the drain's empty. Take a night on the feed line at the berth and it reloads.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_false"}]}}, {"text": "Reach for it at the finish.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 1}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_false"}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "lt", "value": 1}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 1}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 2}]}}, {"text": "Reach for it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 3}]}}, {"text": "Reach for it at the finish.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "lt", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}]}}, {"text": "Take it.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_leash_state", "operator": "gte", "value": 2}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "loop_mercer_finisher",
choices: window._engineNoExitsDiag_loop_mercer_finisher,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_loop_mercer_finisher>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas loop_mercer_finisher</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Navigation]]
<</if>>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "controller_off", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "owner_drained", "operator": "is_false"}]})>>
<p>He has the bottle out of the case already and a second glass on the crate that nobody is going to drink from.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Thursday. Look at that. I said Thursday and here's Thursday. That box has been the best week I've had in a year.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "leash_answered", "operator": "is_false"}]})>>
<p>The fourth part has been in her three days and she has stopped thinking about it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There she is. I bought a chair. Two rows over, a man selling chairs to people with no floor to put them on, and I thought — why not.</div></div><p>She doesn't sit in it. He is pleased she came anyway.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "mercer_found", "operator": "is_true"}]})>>
<p>The bulb is on and the bottle is already down off the crate. He has his shoes off, which down here passes for being at home.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There she is. Shut that behind you — the market has ears, and half of them are mine, which is the worse half.</div></div>
<</if>><span id="cascade-b3-beat-0"><<set _bp to random(0, 3)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> A man came in today wanting a ticket dated back four years. I did it in an hour.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two stalls down there's a woman selling boots she hasn't got. I admire it. I couldn't do it.</div></div>
<<elseif _bp is 2>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> The bulb went twice last week. I keep spares now. I never once did that upstairs.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There's a lad two rows over undercutting me on manifests. He'll be gone by winter. They always are.</div></div>
<</if>><p>He sets the glass down and turns her by the shoulder, and the turning is the whole of the asking.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Go on, then. You know how I like it and I know you know, and that is the restful part of it.</div></div></span>
<<set $game_state.current_canvas = "loop_mercer_lockup">>
<<set $game_state.current_node = "loop_mercer_lockup.intro">>
<<script>>setup.markCanvasTriggered("loop_mercer_lockup");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_mercer_lockup.intro") === -1) { vn.push("loop_mercer_lockup.intro"); }<</script>>
<<nobr>>
<<link "Get on your knees." "Canvas_loop_mercer_lockup_Node_base_oral_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Bend over the end of the bed." "Canvas_loop_mercer_lockup_Node_base_vaginal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Give him your ass." "Canvas_loop_mercer_lockup_Node_base_anal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_oral_t5"] to ($game_state.media_cycle["sex_mercer_lockup_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_oral_t5/c021d071d3f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_oral_t5/c271aa0ba1d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_oral_t5/c415c74dd30.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_oral_t5/ce3a7ecf883.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees on the concrete, his cock further back in her mouth than she has room for. He holds her down on it a second at a time, unhurried, until her nose is at his balls. She sucks him through it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Every time you come now. Two years of never, and now every time.</div></div>
<<set $game_state.current_canvas = "loop_mercer_lockup">>
<<set $game_state.current_node = "loop_mercer_lockup.base_oral_mercer">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_mercer_lockup.base_oral_mercer") === -1) { vn.push("loop_mercer_lockup.base_oral_mercer"); }<</script>>
<<nobr>>
<<link "Keep him in your mouth." "Canvas_loop_mercer_lockup_Node_base_oral_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Get up and bend over the end." "Canvas_loop_mercer_lockup_Node_base_vaginal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_lockup:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_loop_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_lockup:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_routine_t5"] to ($game_state.media_cycle["sex_mercer_lockup_routine_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_routine_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_routine_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_routine_t5/c8bc5179335.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_routine_t5/c8fad95b0cf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_routine_t5/c63f9b3485d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_routine_t5/ca7213f0fbd.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He gets his cock into her cunt and settles into it, unhurried, one hand flat between her shoulder blades and the other still holding the glass. Her cunt is wet and takes the whole length of him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Same as ever. I like that there's a same-as-ever left. Not one thing about this has changed.</div></div>
<<set $game_state.current_canvas = "loop_mercer_lockup">>
<<set $game_state.current_node = "loop_mercer_lockup.base_vaginal_mercer">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_mercer_lockup.base_vaginal_mercer") === -1) { vn.push("loop_mercer_lockup.base_vaginal_mercer"); }<</script>>
<<nobr>>
<<link "Let him have it." "Canvas_loop_mercer_lockup_Node_base_vaginal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_loop_mercer_lockup_Node_base_oral_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him take your ass instead." "Canvas_loop_mercer_lockup_Node_base_anal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_lockup:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_loop_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_lockup:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_anal_t5"] to ($game_state.media_cycle["sex_mercer_lockup_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_anal_t5/c1bd0bd2bb0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_anal_t5/c4cf27c1489.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_anal_t5/c534167cfb9.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_anal_t5/cce4ff8e7cd.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He puts his cock in her ass the way he always got round to it, and she opens for him out of two years of practice. He is thick and does not rush it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> That's the one. That was always the one. You never made a fuss about this.</div></div><p>She gets a hand back around his wrist, skin on skin, and holds it there while he fucks her ass. That is the seat — the drain sits in the contact and waits for the finish.</p>
<<set $game_state.current_canvas = "loop_mercer_lockup">>
<<set $game_state.current_node = "loop_mercer_lockup.base_anal_mercer">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_mercer_lockup.base_anal_mercer") === -1) { vn.push("loop_mercer_lockup.base_anal_mercer"); }<</script>>
<<nobr>>
<<link "Take it in the ass." "Canvas_loop_mercer_lockup_Node_base_anal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back on him." "Canvas_loop_mercer_lockup_Node_base_vaginal_mercer">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_mercer_lockup:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — and reach for it." "Canvas_loop_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_mercer_lockup:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><video src="./videos/scenes/kess_install.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The bench is clear and the lamp is down low over it.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> First one. Everything I could read off that lock while it was open, cut down to sit where the drain sits.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 2}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Sweep head. Wider than the last one — I'll have to take more of the seam. Lie still and breathe.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 3}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Isolator. Heavy for its size, and it sits deeper. If it bites this time it bites this, not you.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Last one. Stacked — three seatings in one seam. Hold still. After this you're free or I'm out of ideas.</div></div>
<</if>><span id="cascade-b6-beat-0"><p>He clears a space with his forearm and folds a coat at one end for her face.</p><span id="cascade-b6-beat-1"><<linkreplace "Get on the bench.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Down. Face in that, arms flat at your sides. I work quicker when I'm not asking.</div></div><p>She takes her trousers down and lies face-down on the steel, bare from the hip, because that is where the seam is.</p><span id="cascade-b6-beat-2"><<linkreplace "Hold still.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Colder than the cradle. I'd rather steel than a pad I can't wipe down.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Arms stay where they are. You move while I'm in the seam and I close it on my own thumb.</div></div><p>His hand finds the ridge without hunting for it.</p><span id="cascade-b6-beat-3"><<linkreplace "Let him open it.">><p>It gives the way it gave for Bastien's men — patient, a quarter turn, then all at once.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There it is. Same as it was. Whoever built you liked a clean edge.</div></div><span id="cascade-b6-beat-4"><<linkreplace "Wait.">><p>The whole lower half of her goes to something that is not pain and is not pressure, and has no word, because she was never built to be given one.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Breathe normal. You're holding it, and holding it tightens the seam on me.</div></div><span id="cascade-b6-beat-5"><<linkreplace "Breathe.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This is the part where a person swears at me. You lie there like a bench vice.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Not a complaint. Makes it quick.</div></div><p>His hands go in past the second knuckle and stop being hands.</p><span id="cascade-b6-beat-6"><<linkreplace "Feel him working.">><p>Something is happening inside her housing at a depth nothing has ever reached from the outside, unhurried, and she can follow every inch of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Seating it now. It wants to sit where the drain sits, which is the reason this might work and the reason it might cook you.</div></div><span id="cascade-b6-beat-7"><<linkreplace "Stay still.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Bastien's men opened this to take something out, and they laughed while they did it. This one is putting something in and telling her to breathe. Her body cannot find the difference. Only the room can.</div></div><p>It seats with a small definite pressure, the way a bolt seats, and stays. He presses the ridge flat with his thumb twice and steps back to the lamp.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That's in. It sits until it's used or it's cooked. Go and get it used.</div></div>[[Get dressed.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "kess_install_part">>
<<set $game_state.current_node = "kess_install_part.base">>
<<script>>setup.markCanvasTriggered("kess_install_part");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("kess_install_part.base") === -1) { vn.push("kess_install_part.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "part_held", "set", 0.0, true, null);setup.applyAndNotifyTrait("player", null, "part_installed", "set", 1.0, true, null);setup.showEffectNotification();<</script>><p>The key is where he left it, on the cloth, dead. He has not put it away. He has been waiting to find out whether it mattered.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Well?</div></div><span id="cascade-b1-beat-1"><<linkreplace "Tell him it worked.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It caught. Nothing came to shut it.</div></div><p>He sits back off the bench for the first time she has ever seen and lets out a breath through his nose.</p><span id="cascade-b1-beat-2"><<linkreplace "Tell him the rest.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> He said his only ever spoke for him. Like there were others.</div></div><p>The breath goes back in. He looks at the dead thing on the cloth for a while.</p><span id="cascade-b1-beat-3"><<linkreplace "Let him get there.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Then I've killed a lock and you're still locked. That's not a small thing, but it's the wrong thing to have done well.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Make him say it plainly.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It was never the key. It's what's IN you. That answers to anything with the right voice, and I've got no idea how many of those are walking round this city.</div></div><span id="cascade-b1-beat-5"><<linkreplace "Ask what he wants.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> So what do you want.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I want it back on. A dead lock tells me nothing — I need it talking, with you underneath it, or I'm guessing at the thing in you for the rest of your life.</div></div><span id="cascade-b1-beat-6"><<linkreplace "Let it sit.">><p>He does not fill the gap. He has never once filled a gap for her, and she has come to read that as the closest he gets to respect.</p><span id="cascade-b1-beat-7"><<linkreplace "Say yes.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Turn it on.</div></div><p>He picks the key up and it wakes under his thumb, and the old weight comes back down over her the way a coat does, and she stands there and lets it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Right. Then it's a part a go, off the small stalls, and a bench, which is me, and I won't guess how many. Bring me a talkback.</div></div><p>He goes back under the lamp. She has bought herself a leash to stand in until he is done, and she chose it, which is new.</p>[[Leave him to it.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "kess_the_chip_not_the_key">>
<<set $game_state.current_node = "kess_the_chip_not_the_key.base">>
<<script>>setup.markCanvasTriggered("kess_the_chip_not_the_key");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("kess_the_chip_not_the_key.base") === -1) { vn.push("kess_the_chip_not_the_key.base"); }<</script>>
<<script>>advanceTime(45);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "parts_loop_open", "set");<</script>><img src="./videos/locations/kess_berth.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He has not been to bed. She can tell by the bench, which has been tidied the way a man tidies when he is close to something.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's open.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Wait for the rest.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Your box kept his hand the way it's built to. I fed that to the lock and the lock took it. It has no way of telling a hand off a courier plate from a hand in the room.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Ask what that means.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> So it's off.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This is off. That's all I'll say standing here. The key's stopped talking — what that does to the thing in you is a different sentence and I'm not saying it yet.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Make him say the rest.">><p>He turns the key over once, the way he turns anything he has finished with, and sets it down on the cloth.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It never had to be near you. Two years in a coat pocket and it worked on you across a city. Off carries the same distance.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Ask how she'd know.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then how do I know.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You go and reach for it on the man it never let you touch. It catches or it doesn't. There's no reading I can take off this bench that beats that one.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Wait for him to ask.">><p>He doesn't ask who the man is. He hasn't asked once, and she has stopped expecting him to start.</p><span id="cascade-b2-beat-6"><<linkreplace "Let it go.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Two years of a thing in her that answered to a man who never once asked what it was for, and it came off a courier box and a hand he gave away for nothing.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Go on, then. Come back and tell me which one of us was right.</div></div><p>He goes back under the lamp. That is the whole of the goodbye down here, and she has stopped minding it.</p>[[Go and find out.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "kess_print_read">>
<<set $game_state.current_node = "kess_print_read.base">>
<<script>>setup.markCanvasTriggered("kess_print_read");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("kess_print_read.base") === -1) { vn.push("kess_print_read.base"); }<</script>>
<<script>>advanceTime(45);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "controller_off", "set");<</script>><img src="./videos/scenes/mercer_print.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He is pouring before the door is properly shut.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Take that off. It's warm in here — warmer than out there, anyway, which is the one thing this room does better than the last one.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Take the coat off.">><p>She hangs it over the crate. The bottle goes down onto the floor to make room, and he watches her do it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Nine years I had somebody take a coat off me at a door. Now I hang my own on a nail. You don't miss it until it's gone.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Wait for him to come over.">><p>He doesn't come over. He pours the second one he has no intention of drinking and looks at her from the crate, taking his time, the way a man looks at something he already owns.</p><span id="cascade-b2-beat-3"><<linkreplace "Go to him.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come here.</div></div><p>He gets a hand into her hair without hurry, the way a man takes hold of a thing he has owned a long time.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two years and I never once had to raise my voice at you. People don't understand what that is worth.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Let him kiss you.">><p>He kisses her, and it is the first time anyone has in two years. He does it the way he does everything, unhurried and without asking, his fist still closed in her hair.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I never did that enough. I thought about it often and I never did it.</div></div><span id="cascade-b2-beat-5"><<linkreplace "Let him take his time.">><p>He works her out of the rest of it one piece at a time, both hands, no impatience anywhere in him. He sets each thing on the crate instead of dropping it. Nobody has undressed her slowly in two years.</p><span id="cascade-b2-beat-6"><<linkreplace "Let him have your tits.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_tits_t5"] to ($game_state.media_cycle["sex_mercer_print_tits_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_tits_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_tits_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_tits_t5/c19a60d3b93.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_tits_t5/ca45c6c5abb.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_tits_t5/cada734d54d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_tits_t5/cb2ad6314a6.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She stands naked on the concrete and lets him. He gets a hand on her tits, weighing one and then the other, working a nipple with his thumb, and never looks down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two years and I never had an evening with nowhere to be at eight.</div></div><span id="cascade-b2-beat-7"><<linkreplace "Let him turn you round.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_ass_t5"] to ($game_state.media_cycle["sex_mercer_print_ass_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_ass_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_ass_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_ass_t5/c9b9c58197d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_ass_t5/c084c5cdb6b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_ass_t5/ce78552a97d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_ass_t5/cf3adfb7dc6.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He turns her round by the hip and gets both hands on her ass, weighing it the way he weighed her tits, squeezing hard enough to leave marks and letting go, unhurried.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You always did have the best ass in that building. I signed for that too.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Go down on him.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_knees_t5"] to ($game_state.media_cycle["sex_mercer_print_knees_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_knees_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_knees_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_knees_t5/c6df7a06f02.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_knees_t5/c7a743c8f23.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_knees_t5/c61c2696ccc.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_knees_t5/c6564bf8a0e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He puts a hand on her shoulder and presses her down onto the concrete. His cock is out before she settles. She sucks it. He fucks her mouth slowly and keeps talking.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> That's a new one. Two years and you never once. I'd have thought about it and not asked.</div></div><span id="cascade-b2-beat-9"><<linkreplace "Let him hold you there.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_throat_t5"] to ($game_state.media_cycle["sex_mercer_print_throat_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_throat_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_throat_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_throat_t5/c8c8262b84e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_throat_t5/c595cc07ad4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_throat_t5/ca40ac90429.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_throat_t5/cc4a48fb077.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He gathers her hair in his fist and holds her down on his cock until her nose is against his balls, and keeps her there through a whole sentence. Her eyes run. She moans around him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Up. Come on. I want you the other way now.</div></div><span id="cascade-b2-beat-10"><<linkreplace "Stand still for it.">><p>His hand goes flat on her stomach and down, and two fingers find her cunt already wet, in to the knuckle, the heel of his palm hard on her clit until her cunt grips down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There. I remember that. That never was for show, whatever else was.</div></div><span id="cascade-b2-beat-11"><<linkreplace "Let him.">><p>He turns her by the hip and puts her over the end of the bed one-handed, because the other one still has the glass in it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Stay like that. God. Yes — like that.</div></div><span id="cascade-b2-beat-12"><<linkreplace "Let him have it.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_lockup_crate_t5"] to ($game_state.media_cycle["sex_mercer_lockup_crate_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_lockup_crate_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_lockup_crate_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_lockup_crate_t5/c73b1a72c92.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_lockup_crate_t5/ca0a90e2b47.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_lockup_crate_t5/ca644e68baa.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_lockup_crate_t5/cd9ba54840d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He gets his cock into her and starts up unhurried, one hand spread flat on the small of her back out of old habit. Her cunt takes the whole thick length of him the way it always did, and she moans into the blanket.</p><span id="cascade-b2-beat-13"><<linkreplace "Take it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There. There she is. Not one thing about this has changed, and in the state I'm in that is worth more to me than the floor ever was.</div></div><p>He fucks her steady, no hurry anywhere in it, and her cunt answers the way it always has.</p><span id="cascade-b2-beat-14"><<linkreplace "Let him talk.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> That glass cost more per pane than this whole row of stalls is worth. I signed for every one of them. I signed for you the same week. Did you ever know that?</div></div><p>He doesn't wait to find out. He never has.</p><span id="cascade-b2-beat-15"><<linkreplace "Let him lean.">><p>He shifts his weight and puts his free hand flat on the crate beside her coat, and leans on it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> The plants woman. Thursdays. I never learned her name either, and it has started to bother me at night.</div></div><span id="cascade-b2-beat-16"><<linkreplace "Wait.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> His hand has been flat on that crate a while now. It's the stillest part of him.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Are you listening. You always did let a man get to the end of a thing.</div></div><span id="cascade-b2-beat-17"><<linkreplace "Let him have your ass.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_anal_t5"] to ($game_state.media_cycle["sex_mercer_print_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_anal_t5/c2ff9766ca5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_anal_t5/c20dfe1e529.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_anal_t5/c95ae78cb46.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_anal_t5/cd52b8a5539.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He pulls out of her cunt and puts his cock in her ass instead, the way the evening always used to end. She opens for him out of two years of practice, and he does not rush that either.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There. That's the one I was thinking of.</div></div><span id="cascade-b2-beat-18"><<linkreplace "Let him finish.">><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_print_anal_finish_t5"] to ($game_state.media_cycle["sex_mercer_print_anal_finish_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_print_anal_finish_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_print_anal_finish_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_print_anal_finish_t5/c037160e284.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_print_anal_finish_t5/cc5bb5d54ad.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_print_anal_finish_t5/ce30fc9434b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_print_anal_finish_t5/cf08e100143.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>His cock goes deep in her ass and stays there while he cums, and lets a breath out like a man sitting down. His weight stays forward, on the hand on the crate.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Ah. That's better. That is the first quiet hour I have had in a month.</div></div><span id="cascade-b2-beat-19"><<linkreplace "Reach for it.">><p>She reaches down inside for the old pull — the thing that used to open a man at the base of the spine. It closes on nothing. Not a flicker. Whatever he is now, she cannot get at it.</p><span id="cascade-b2-beat-20"><<linkreplace "Then give him the thing you brought.">><p>She gets her coat off the crate and takes the case out of it — grey, courier-grade, a reader plate let into the lid — and puts it in his hands while he is still doing up his belt.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It's bonded. You have to open it yourself.</div></div><span id="cascade-b2-beat-21"><<linkreplace "Let him have it.">><p>He turns it over twice. Nobody has sent him anything since the floor went, and it takes him a moment to work out what is happening to him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Bonded. God. I used to sign for forty of these a week and never once opened one myself.</div></div><span id="cascade-b2-beat-22"><<linkreplace "Let him talk himself down.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> It was never the money. I want to be clear about that. It was never once about the money.</div></div><p>He says it to the box, not to her.</p><span id="cascade-b2-beat-23"><<linkreplace "Watch him open it.">><p>He spreads his palm flat on the plate, holds it there, and the reader goes warm and takes him. The lid gives. He lifts the bottle out and does not look at the box again.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You liked that floor. You are the only one left I can say that to.</div></div><span id="cascade-b2-beat-24"><<linkreplace "Pick up the empty.">><p>The case goes on the floor by his foot the way anything does once it's open. She folds it under her arm on her way past, tidying up after a man who has stopped tidying up after himself.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Leave it, leave it. God, you don't have to do that.</div></div><p>His cum runs out of her ass and down her thigh and she leaves it. He sits back with the bottle in his fist, pleased with it, and doesn't put it down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Thursday. It doesn't have to be Thursday. Come whenever — you know where the door is now.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Thursday.</div></div>[[Get your coat.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_owner_print">>
<<set $game_state.current_node = "cap_owner_print.base">>
<<script>>setup.markCanvasTriggered("cap_owner_print");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_owner_print.base") === -1) { vn.push("cap_owner_print.base"); }<</script>>
<<script>>advanceTime(90);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "case_held", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "owner_print_taken", "set");<</script>><p>He is most of the way through the one about the car and the driver when he stops.</p><span id="cascade-b1-beat-0"><p>He sets the glass down on the crate and doesn't pick it back up. Outside, somebody drags a shutter down and swears at it.</p><span id="cascade-b1-beat-1"><<linkreplace "Wait.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I never had to ask you for anything. Not once in two years. I'd think of it and it would be done, and I never gave that the credit it was owed.</div></div><p>He says it the way a man says a fact about the weather.</p><span id="cascade-b1-beat-2"><<linkreplace "Say his name.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Mercer.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Mm.</div></div><p>He doesn't correct her. He doesn't lower his voice, and he doesn't look at the door.</p><span id="cascade-b1-beat-3"><<linkreplace "Let him get to it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> It was a good arrangement. Say what you like about the rest of that place — that part of it worked, and it worked for both of us, whatever anyone down here would tell you.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Watch his hand.">><video src="./videos/scenes/mercer_hands_on_t4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>His hand comes over and settles on her thigh, above the knee, with the whole weight of it behind it. Not a grab. The way a man rests a hand on something of his.</p><span id="cascade-b1-beat-5"><<linkreplace "Don't move.">><p>She doesn't move. He never looks up to see whether she will.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You've not changed. That's the thing about you. Everything else went and you didn't, and I find I need that more than I'd like to.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Where was I. The driver. I never did learn the man's name.</div></div><p>He picks it up exactly where he put it down, and his hand stays where it is.</p>[[Sit it out.->Location_mercer_room]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "rung_mercer_hands_on">>
<<set $game_state.current_node = "rung_mercer_hands_on.base">>
<<script>>setup.markCanvasTriggered("rung_mercer_hands_on");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_mercer_hands_on.base") === -1) { vn.push("rung_mercer_hands_on.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "mercer_hospitality_open", "set");<</script>><p>He holds the corner table like it is still an office — one hand on the glass, the other one out, explaining something to a room that did not ask.</p><<set _bp to random(0, 2)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Sit down. I'll get you a — no. Right. You don't. Two years and I still reach for the second glass.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> The one at the bar in the good coat. Two months ago he had eleven people under him. Now he counts what's in his pocket before he orders. This room is full of us.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Man coming Thursday wants a badge history that stands up to a real check. He'll pay what I ask because there isn't a second person down here who can do it.</div></div>
<</if>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_mercer", "trait_key": "relation", "operator": "lt", "value": 6}]})>>
<p>He leans in, and the volume goes out of him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> The name. I did ask. I'm asking properly this time — not in here, not where the room can hear it. It costs me if it travels, and it costs me more than you'd think.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Who would it travel to, Mercer.</div></div><p>He looks at the room for a second. Then he answers the question.</p>
<</if>>
<<set $game_state.current_canvas = "mercer_end_table">>
<<set $game_state.current_node = "mercer_end_table.base">>
<<script>>setup.markCanvasTriggered("mercer_end_table");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_end_table.base") === -1) { vn.push("mercer_end_table.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_mercer", "relation", "add", 2.0, false, null);setup.showEffectNotification();<</script>>
[[Let him hold court.->Location_underworld_bar]]<p>A padlock through a hasp, and the stall in front of it shuttered. The room is only his after the market goes dark — eleven at night until eight, and he is somewhere else the rest of it. He keeps late hours now. Everyone down here does, and he has started calling it a preference.</p>
<<set $game_state.current_canvas = "mercer_room_offhours">>
<<set $game_state.current_node = "mercer_room_offhours.base">>
<<script>>setup.markCanvasTriggered("mercer_room_offhours");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_room_offhours.base") === -1) { vn.push("mercer_room_offhours.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Back into the market.->Location_underworld_market]]<img src="./videos/scenes/mercer_room_hub.webp" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>One bulb, a bed against the wall, a crate doing duty as a table. The bottle on it is better than the room.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "mercer_hospitality_open", "operator": "is_false"}]})>>
<p>He is up off the bed before the door is shut, moving the crate a foot to the left as if that made it a table.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Sit. Sit down. It's not what you're used to — neither am I, come to that, and I'd rather you saw it than heard about it.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "mercer_hospitality_open", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "parts_loop_open", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "part_installed", "operator": "lt", "value": 1}]})>>
<p>He doesn't get up now. He nods at the crate and goes back to the bottle, and the room arranges itself around him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There she is. Shut the door behind you — it's a market out there and half of it listens for a living.</div></div><p>Her seam is empty under the coat — the old dead socket, nothing seated in it. The part is still in her pocket, or still on Kess's bench, and it does not work from either.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "mercer_hospitality_open", "operator": "is_true"}]})>>
<p>He doesn't get up now. He nods at the crate and goes back to the bottle, and the room arranges itself around him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There she is. Shut the door behind you — it's a market out there and half of it listens for a living.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_mercer_room">>
<<set $game_state.current_node = "hub_mercer_room.base">>
<<script>>setup.markCanvasTriggered("hub_mercer_room");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_mercer_room.base") === -1) { vn.push("hub_mercer_room.base"); }<</script>>
<<nobr>>
<<link "Let him talk." "Canvas_hub_mercer_room_Node_talk">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Pour for him." "Canvas_hub_mercer_room_Node_pour">><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "owner_print_taken", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('hub_mercer_room:cc0') ? '' : 'unlocked-choice'">
<<link "Give him the evening." "Canvas_loop_mercer_lockup_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_mercer_room:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave him to it." "Location_mercer_room">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<set _bp to random(0, 3)>>
<<if _bp is 0>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Nine years on that floor. You know what the glass cost? Per pane. I know it per pane because I signed for it, same as I signed for you. A man remembers what he signs for.</div></div>
<<elseif _bp is 1>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> There was a woman came up Thursdays to do the plants. Plants. In a building that made its money out of other buildings. I used to think that was the whole point of the place.</div></div>
<<elseif _bp is 2>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I had a car and a driver and I never once learned the driver's name. That wasn't cruelty, it simply never came up. Now I'd like to know it and there's nobody left to ask.</div></div>
<<else>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> They gave me a floor the week I was useful and took it back the week I wasn't. I don't hold it against them. I'd have done it faster, and I'd have done it on a Friday.</div></div>
<</if>><p>None of it is about her. He is lonely for the man who had a floor, not for her — and she is the only piece of that left in the room.</p>
<<set $game_state.current_canvas = "hub_mercer_room">>
<<set $game_state.current_node = "hub_mercer_room.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_mercer_room.talk") === -1) { vn.push("hub_mercer_room.talk"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_mercer", "relation", "add", 2.0, false, null);setup.showEffectNotification();<</script>>
[[Let it run.->Location_mercer_room]]<p>She takes the bottle off the crate and fills his glass. He watches her hand do it and doesn't thank her, because thanking her never was part of it.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_mercer", "trait_key": "relation", "operator": "lt", "value": 12}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> You don't have to do that. ...No, go on. It's nice to have it done properly for once. Down here a man pours his own and calls it independence.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_mercer", "trait_key": "relation", "operator": "gte", "value": 12}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Again. Go on. This is the only hour of my week that runs the way it's meant to, and I've stopped pretending that's a small thing.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_mercer_room">>
<<set $game_state.current_node = "hub_mercer_room.pour">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_mercer_room.pour") === -1) { vn.push("hub_mercer_room.pour"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_mercer", "relation", "add", 3.0, false, null);setup.showEffectNotification();<</script>>
[[Sit with him.->Location_mercer_room]]<img src="./videos/scenes/mercer_undertow.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The Undertow at this hour is corners and low talk. She is three steps in before she places the man at the end table.</p><span id="cascade-b2-beat-0"><p>Out of the grey suit, into something cheaper. Thinner in the face.</p><span id="cascade-b2-beat-1"><<linkreplace "Cross to him.">><p>He looks up on her second step and is out of the chair before she reaches the table.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Wren. Wren — look at you.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Let him look at you.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Sit down. Sit. What are you drinking — no, don't tell me. You don't. You never did.</div></div><p>He has hold of her arm above the elbow. He isn't checking her over. He is just holding on.</p><span id="cascade-b2-beat-3"><<linkreplace "Sit.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Two years I ran you and I never once saw you walk into a room I hadn't sent you to.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I thought — well. Doesn't matter what I thought. You're standing there.</div></div><span id="cascade-b2-beat-4"><<linkreplace "Say his name.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Mercer.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Yes. Yes, all right.</div></div><p>Something crosses his face and settles again.</p><span id="cascade-b2-beat-5"><<linkreplace "Wait for it.">><p>He leans in. His voice comes down to the level the corners use.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Small thing. Nobody's called me that down here. It's Adler. Adler, all right? Humour an old man his fresh coat of paint.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Answer him.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What are you doing down here, Mercer.</div></div><p>He waits half a second for something to follow it. Nothing does.</p><span id="cascade-b2-beat-7"><<linkreplace "Watch him take it.">><p>Whatever he was going to do with that, he doesn't. He sits back and the pleasure comes straight back into his face, unbroken.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Business. Believe it or not.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Ask what business.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Stall on the market, back end of the row — I sell what I know, and it turns out I know a great deal.</div></div><span id="cascade-b2-beat-9"><<linkreplace "Let him tell it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Half the men down here are running from a floor of that tower and not one of them can read a manifest. I can read a manifest.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> I'm not poor. I want you to know that. I did all right out of it.</div></div><span id="cascade-b2-beat-10"><<linkreplace "Ask nothing.">><p>She doesn't ask. He tells her anyway, briefing her the way he used to — the same voice with the floor taken out from under it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> They cleaned that penthouse out in a day. One day. Nine years I was up there.</div></div><span id="cascade-b2-beat-11"><<linkreplace "Let him get to it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come round. I've a room behind the stall — it's a concrete box, I won't pretend otherwise, but there's a bottle in it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Come and sit with me and we'll talk about when things were run properly. You'll come.</div></div><span id="cascade-b2-beat-12"><<linkreplace "Agree.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> All right.</div></div><div class="thought-bubble thought-bubble-npc"><div class="thought-bubble-content"><em>💭 Npc is thinking:</em> She notices that she said it twice and didn't stop when he asked. She files it with the readings and doesn't look at it again.</div></div><p>He is still talking when she looks down at his hands on the table. Big, soft, a signet he has stopped wearing and the pale band still on the finger.</p><p>She doesn't tell him why she came. She hasn't worked out yet how she takes it off him.</p>[[Leave him talking.->Location_underworld_bar]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_mercer_resurfaces">>
<<set $game_state.current_node = "cap_mercer_resurfaces.base">>
<<script>>setup.markCanvasTriggered("cap_mercer_resurfaces");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_mercer_resurfaces.base") === -1) { vn.push("cap_mercer_resurfaces.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "mercer_found", "set");<</script>><img src="./videos/locations/kess_berth.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>He has the key under the lamp when she comes down, and the file shut in a box beside it. He has been on the small thing, not the big one.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Come here. I've taken this as far as I can on my own.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Ask what he's got.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You said hardware you could beat.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I can. I have. I know what it wants now — it wants a handshake, and I know the shape of the handshake.</div></div><p>She waits for the part he's working up to.</p><span id="cascade-b2-beat-2"><<linkreplace "Let him narrow it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I've had everything off you I'm going to get. You're the receiver. You were never the hard half.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> The other end I can read, but I can't be it.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Hear the correction.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And I priced it wrong when I took it. I said a hand instead of the dead man's — because everyone who signs a machine like you is dead by now.</div></div><p>He sets the key down and looks at her.</p><span id="cascade-b2-beat-4"><<linkreplace "The ask.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> He isn't. So it's the hand I need — his, warm, pressed flat and whole. Not a smudge off a glass. The kind a lock takes and keeps.</div></div><p>The bench is cold under her palm again. She doesn't take it away this time.</p><span id="cascade-b2-beat-5"><<linkreplace "Reach for the key.">><p>Her hand goes towards it and there is not much left to reach for. It is in four pieces on a cloth, with the lamp over the smallest one.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It doesn't go anywhere. It hasn't been a whole thing since Tuesday, and the bit that matters is the bit I can't open.</div></div><span id="cascade-b2-beat-6"><<linkreplace "Say the other reason.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> And he'd know it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There you go. Whoever he is, he had it long enough to know it by the weight. Put that in front of him and you've told him everything about you in one go.</div></div><span id="cascade-b2-beat-7"><<linkreplace "Then how.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then how do I bring you a hand.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Bonded case. The market's full of them — courier boxes that only open to the buyer's hand and keep a record of it, because the record IS the receipt. Get him to open one. Bring me the case.</div></div><span id="cascade-b2-beat-8"><<linkreplace "Wait for the rest.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I don't need his name and I'm not asking for it. That's yours.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I need the hand, and I need him not to make a thing of it while it's happening.</div></div><p>He goes back to the lamp.</p><p>She has known the answer since the day he told her there was a key. She doesn't say it now either.</p><p>Somewhere down here is a man who signed for her, and she is going to go and find his hands.</p>[[Go and find him.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "kess_needs_print">>
<<set $game_state.current_node = "kess_needs_print.base">>
<<script>>setup.markCanvasTriggered("kess_needs_print");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("kess_needs_print.base") === -1) { vn.push("kess_needs_print.base"); }<</script>>
<<script>>advanceTime(20);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "print_needed", "set");<</script>><img src="./videos/scenes/kess_file_shape.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She comes down the ramp and he doesn't look up. The file is open on the steel, weighted flat with a spanner at each corner, and he has been at it a while.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't touch the corners. I've got them where I want them.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Ask what he's got.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> You've read it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Some of it. Enough to tell you what it is, which isn't the same as what it says.</div></div><p>She stays standing. There is a stool a foot from her hip and she does not take it.</p><span id="cascade-b2-beat-2"><<linkreplace "Let him say it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's not a file. A file is one thing about one person. This is a programme — intake sheets, revisions, sign-offs, a whole office of people doing paperwork for years.</div></div><p>Her hand finds the edge of the bench and rests there, flat.</p><span id="cascade-b2-beat-3"><<linkreplace "Hear him rank it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I've stripped Vance freight, Vance medical, Vance security. I've seen their paperwork. It doesn't look like this. This is somebody's life work.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> How long.</div></div><p>The question comes out flatter than she means it to.</p><span id="cascade-b2-beat-4"><<linkreplace "Wait for the number.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Eleven years, start to last entry. The early sheets are handwriting. The late ones are printed. Somebody got funding.</div></div><p>Eleven years is a number she has nowhere to put. Her breathing doesn't change. Something under her sternum does.</p><span id="cascade-b2-beat-5"><<linkreplace "The index.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There's an index. Serials, one to a line, a lot of lines. They don't start at one — whoever began this was already carrying work over from somewhere else.</div></div><p>She looks at the column and does not count it.</p><span id="cascade-b2-beat-6"><<linkreplace "The two columns.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Two date columns against each serial. First one's filled all the way down. Second one's filled most of the way down.</div></div><p>He says it the way he'd read a gauge. Her hand has gone tight on the bench edge and she makes it let go.</p><span id="cascade-b2-beat-7"><<linkreplace "Look at the second one.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> One column for the day they started you. One for the day they stopped. Most of the second column is filled in.</div></div><p>She doesn't ask him to confirm it. He wouldn't.</p><span id="cascade-b2-beat-8"><<linkreplace "Ask what it means.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Say what it means.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> No. I'm telling you what's on it. What it means is a thing people decide, and I've been wrong about people.</div></div><p>That is as close to gentle as he comes.</p><span id="cascade-b2-beat-9"><<linkreplace "Hear the rest.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Huh. Here's the thing I've been chewing on. Every page in this opens for me. Intake, revisions, sign-offs — all of it reads.</div></div><p>He turns one page over with two fingers and lays it flat.</p><span id="cascade-b2-beat-10"><<linkreplace "Let him finish.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> One page doesn't. Yours. Same seal as the key — on one sheet, in a stack where nothing else is locked at all.</div></div><p>Somebody went through a finished programme and shut one page. She feels the cold of the bench come up through her palm.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> So it rides the key, like I said. I open that, you get your page. Until then, this is all there is.</div></div><p>She stays a moment longer than she needs to, then straightens off the bench and goes to find work for her hands.</p>[[Leave it with him.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_file_shape">>
<<set $game_state.current_node = "cap_file_shape.base">>
<<script>>setup.markCanvasTriggered("cap_file_shape");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_file_shape.base") === -1) { vn.push("cap_file_shape.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "file_shape_known", "set");<</script>><img src="./videos/locations/kess_berth.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Black water, the hull on its blocks, the smell of cut steel. Kess is at the bench with the cot behind him and her two locked things somewhere under the mess he calls an order.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "leash_cut", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Bed's paid and the line's on. I've a hull to cut and you've nothing in you that needs me, which is the first time that's been true.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "leash_cut", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "lt", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Line's off. You're nobody's asset now, which doesn't make the cot free — it makes it something you have to want.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You're paid up, so I'm on it. Don't hover.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You've come back empty, so it went. Sit down and tell me what it did before you go spending on the next one.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "gte", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You've got something on you that came off a stall. Bring it here when you want it in — it's a bench job, not a doorway job, and you'll be face-down on the cradle for it.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "lt", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Your key's where you left it. So's my hand. You've been sleeping rough — that buys you a floor, not me.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "gte", "value": 1}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You bought the part. Good. I still don't seat hardware on a bench that isn't paid — take a proper night on the line first, then bring it back and I'll have it in you inside the hour.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_kess_berth">>
<<set $game_state.current_node = "hub_kess_berth.base">>
<<script>>setup.markCanvasTriggered("hub_kess_berth");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_kess_berth.base") === -1) { vn.push("hub_kess_berth.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "leash_cut", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('hub_kess_berth:cc0') ? '' : 'unlocked-choice'">
<<link "Ask how the work's going." "Canvas_hub_kess_berth_Node_work">><<script>>setup.markChoiceVisited("hub_kess_berth:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "chip_read", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('hub_kess_berth:cc1') ? '' : 'unlocked-choice'">
<<link "Ask about the chip." "Canvas_hub_kess_berth_Node_chip">><<script>>setup.markChoiceVisited("hub_kess_berth:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "spec_pending", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('hub_kess_berth:cc2') ? '' : 'unlocked-choice'">
<<link "Tell him the part failed." "Canvas_hub_kess_berth_Node_debrief">><<script>>setup.markChoiceVisited("hub_kess_berth:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "part_held", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('hub_kess_berth:cc3') ? '' : 'unlocked-choice'">
<<link "Give him the part." "Canvas_kess_install_part_Node_base">><<script>>setup.markChoiceVisited("hub_kess_berth:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave him to it." "Location_kess_berth">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "gte", "value": 1}]})>>
<p>He turns the controller over under the lamp without being asked, which is as close to an answer as he gets.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's a handshake, not a lock. It wants to hear a hand it knows. I'm learning what it listens for — off you, mostly, since you're the only receiver in the room.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Keep the line paid and I keep at it. That's the whole arrangement. You'll know when I've got something because I'll say so.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "feed_line_days", "operator": "lt", "value": 1}]})>>
<p>The key is exactly where it was last time, in the same tray, under the same rag. He hasn't had it under the lamp in days.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's going nowhere. I don't work a bench I'm not paid for — that's not me being hard, it's me still being here in a year.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Take a night on the line, proper, and I'll pick it back up. Crash rough all you like — that keeps you upright. It doesn't keep me interested.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Ten coin a night. The House pays that for one man. She files it where she files everything and goes to work.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_kess_berth">>
<<set $game_state.current_node = "hub_kess_berth.work">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_kess_berth.work") === -1) { vn.push("hub_kess_berth.work"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Leave him to it.->Location_kess_berth]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 2}]})>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Nothing happened. He finished, I reached, and there was nothing there to push against.</div></div><p>Kess stops what he is doing, which he has not done for anything she has said before, and looks at the bench rather than at her.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Nothing at all. That's the useful kind. A lock pushes back — yours didn't, so it isn't locking, it's listening. I shouted on the wrong wire.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't buy me another talkback. I want a sweep head — the flat ones off a salvage board, the kind that listens across instead of shouting the once. Ask him for a sweep.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "lt", "value": 3}]})>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It found me. Something came down on it and shut it, hard, and I had to lie there with his weight on me and not make a sound.</div></div><p>He turns her round by the shoulder, unhurried, and reads the seam at the base of her spine with two fingers before he answers.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It bit you. That's not a safety — safeties fail open. That's a guard, and a guard means somebody's still using the thing.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Sweep was right and it got caught listening. So the next one takes the bite instead of you. An isolator, off the same tables. Small, heavy, and the man will tell you it's dead stock.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "mercer_attempts", "operator": "gte", "value": 3}]})>>
<div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It let go. Then something went down a list, found the hole where I am, and shut the door on its way back past me.</div></div><p>That is the first thing she has told him that he writes down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> A list. Then it was never keyed to a man at all — it's a set of voices, and his was one of them. That's why one answer was never going to be enough.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Last one, then. A stacked talkback — holds more than one voice at once. Bring me that and if it takes, there's nothing left in you that answers to anybody.</div></div>
<</if>>
<<set $game_state.current_canvas = "hub_kess_berth">>
<<set $game_state.current_node = "hub_kess_berth.debrief">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_kess_berth.debrief") === -1) { vn.push("hub_kess_berth.debrief"); }<</script>>
<<script>>advanceTime(15);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "part_spec", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "spec_pending", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Go and find it.->Location_kess_berth]]<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Still where you left it and still shut. I've had a light on it twice and I've learned the same nothing both times.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's not a thing you open. It's a thing somebody's paperwork opens — your page came up because the two were cut together, and I'd want the rest of that paper before I went at it with anything sharp.</div></div>
<<set $game_state.current_canvas = "hub_kess_berth">>
<<set $game_state.current_node = "hub_kess_berth.chip">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_kess_berth.chip") === -1) { vn.push("hub_kess_berth.chip"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Leave it with him.->Location_kess_berth]]<img src="./videos/scenes/kess_berth_night.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The berth is where she left it — black water, the stripped hull up on the blocks, Kess at the cradle with a torch in his hand. He doesn't ask why she's back. He reads the topside off her the way he read the wrongness off her core, and goes still.</p><span id="cascade-b2-beat-0"><p>Before she says a word she pulls the file out and thumbs it open under his work-light — because she has to know. She gets three pages in. Long enough to see her own designation on it, older than she is, and the word BUILD where a birth would go. Then the pages turn to garble: keyed, sealed, a wall dropped exactly where the answer starts.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> It's about her. It's the whole of her. And she can't read a line of the part that matters.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Put both on his bench.">><p>She sets two things down on the steel beside him: the sealed file, and the little controller she took off Mercer on the way down. Kess wipes his hands, picks up the controller, turns it in the light. Then he thumbs the file, sees the same locked garble, and sets them side by side like two parts off the same machine.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Huh. Same lock. This little key and that file are cut to the same hand — whoever sealed one sealed the other. Makes sense. Same people built you and wrote you up.</div></div><span id="cascade-b2-beat-2"><<linkreplace "Ask if he can open them.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Can you get into them.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Into the key, eventually — it's hardware, and hardware I can beat. Get that reading his hand instead of the dead man's and it cuts your leash from your side. The file rides on the same crack, so it opens when the key does. But it's not a night's work, and I don't guess at timelines on a thing that takes you with it if I'm wrong. It takes what it takes.</div></div><span id="cascade-b2-beat-3"><<linkreplace "Tell him the other problem.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then take the time. I've got nowhere to be. The Spire's shut over me — I can't go back up, and I can't be seen looking for a bed. I need somewhere down here. I'll pay for it. Coin, not favors.</div></div><p>Kess looks at the hull, at the cradle rigged for something bigger than a person, at her. Doing the sum a man does before he lets a problem sleep under his roof.</p><span id="cascade-b2-beat-4"><<linkreplace "Wait for his terms.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There's a cot by the feed line. It's not the box you're used to — half a charge if you're cheap, a full night on the line if you pay for it. You pay per night, in coin, and you keep your hands off my gear. Coin dries up, so does the arrangement. We clear?</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Clear.</div></div><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> A cot in a breaking-dock, rented by the night off a man who owes her nothing. It's the safest she's been in two years — because for the first time the key to her is on a bench she can watch instead of in a pocket she can't reach. She'll pay his coin and wait him out. Down here, waiting is a thing she owns.</div></div>[[Set down. This is home now, for a while.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "kess_1b_open">>
<<set $game_state.current_node = "kess_1b_open.base">>
<<script>>setup.markCanvasTriggered("kess_1b_open");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("kess_1b_open.base") === -1) { vn.push("kess_1b_open.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "berth_home", "set");<</script>><img src="./videos/scenes/salvage_verdict.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess closes the last panel and steps back, wiping his hands, and looks at Wren the way he'd look at a hull he's decided is finished — not fixed. Finished.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That's you operational. Leg holds, drain fires clean, core takes a charge without cooking itself. You're a weapon again. Congratulations.</div></div><span id="cascade-b3-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> But I told you there was something down there I didn't like. I had my proper look while I had you open. And here's the thing — it's not damage. Everything else they broke or wore out. This they built.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Built what.</div></div><span id="cascade-b3-beat-1"><<linkreplace "Hear it.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> A governor. A bad-tempered little piece of hardware seated under your core, older than the rest of you, wired so deep it's part of the frame. It's not there to run you. It's there to stop you — one specific thing you're built so you cannot do.</div></div><span id="cascade-b3-beat-2"><<linkreplace "What thing.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There's a man you can't point your teeth at. You read every other body I put on this cradle by the end — but the one who owns you, you couldn't drain him if he was open on this table in front of you. That's not you being loyal. That's not you being scared. That's this.</div></div><span id="cascade-b3-beat-3"><<linkreplace "Take it out, then.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Then take it out.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Can't. It's keyed — pull it blind and it takes you with it, and I don't do wakes. There's a controller for it somewhere. A key. Whoever holds it cuts it clean in a minute. I don't have it. You don't. Not my business whose it is.</div></div><span id="cascade-b3-beat-4"><<linkreplace "You already know whose.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> He signs for me. He's held the key the whole time.</div></div><p>She doesn't ask him whose. She already has the shape of the answer, and it's the shape of the man whose name is on her papers — the one who's spent every order she ever took knowing she could never turn and make him stop.</p><span id="cascade-b3-beat-5"><<linkreplace "Feel it settle.">><p>Something settles into place at the root of her that isn't comfort. She can feel exactly where it sits now — a locked thing, one door, and no key on her side of it. She'll carry it. A thing she can see is a thing she can carry.</p><span id="cascade-b3-beat-6"><<linkreplace "Settle up.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> We're square — you paid as you went, most don't. Go on. You've got a key to find.</div></div><span id="cascade-b3-beat-7"><<linkreplace "Stand up.">><p>She stands off the cradle. The leg holds, the drain sits true. For the first time she knows the exact shape of the thing that's owned her — not a feeling, a component, with a key, and the key is somewhere she can walk to and take. That isn't despair. It's a map.</p><span id="cascade-b3-beat-8"><<linkreplace "And under that.">><p>And under that, further down, the name from the leak — the one syllable that fit her like a socket and then slipped. She still can't hold it. She's just stopped being able to stop reaching for it.</p>[[Go to work.->Location_the_waterfront]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "salvage_verdict">>
<<set $game_state.current_node = "salvage_verdict.base">>
<<script>>setup.markCanvasTriggered("salvage_verdict");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_verdict.base") === -1) { vn.push("salvage_verdict.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "core_sealed", "set", 1.0, false, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "salvage_done", "set");setup.applyAndNotifyFlag("player", null, "mission_3_active", "set");<</script>><p>Wren's off the cradle, catching her breath, when Kess stops with his hand still in the open panel at the base of her spine. He's gone still the way a man goes still when the wrench finds a bolt that shouldn't be there.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Hold on.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There's something down here I don't like. Wired deeper than the rest of you — under the core, not in it. Older, too. It's not what's stopping the drain, so I've been working around it. But I want a proper look before I close you up for good.</div></div><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What is it?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't know yet. That's what I don't like. Finish getting your teeth back, then we'll talk about it.</div></div>
<<set $game_state.current_canvas = "salvage_chip_snag">>
<<set $game_state.current_node = "salvage_chip_snag.base">>
<<script>>setup.markCanvasTriggered("salvage_chip_snag");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_chip_snag.base") === -1) { vn.push("salvage_chip_snag.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Later, then.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_12_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a last body waiting — a dock-hand, paid, unbothered. He doesn't look worried this time. The readout in his hand is already half-green before Wren's even crossed the floor.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's warm. Warmer than it's ever read on my bench. This is the last one — I just need to see it hold under load. So turn it over, use him, and reach for the take at the finish.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> One clean pull and you're done. Don't fight it this time. Let it work.</div></div>
<<set $game_state.current_canvas = "salvage_session_12">>
<<set $game_state.current_node = "salvage_session_12.base">>
<<script>>setup.markCanvasTriggered("salvage_session_12");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_12.base") === -1) { vn.push("salvage_session_12.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_12_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_12_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The dock-hand's efficient — bends her over the cradle, takes her from behind, sets a steady pace and holds it. "Strangest job I've had," he says over his shoulder, half a laugh. Kess doesn't answer. He's watching the needle sit flat and green.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Steady. It's not even flickering. Take him to the finish.</div></div>
<<set $game_state.current_canvas = "salvage_session_12">>
<<set $game_state.current_node = "salvage_session_12.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_12.n2") === -1) { vn.push("salvage_session_12.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_12_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_12_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes on her and she reaches — the old pull, down at the base of the spine. No cold socket this time, no clawing after a dead thing. His secrets walk out of him in one clean draw. It opens easy; the take barely costs her a want.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. Full pull, right through the finish, no strain on the readout at all. That's whole. That's a clean core.</div></div><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's trim now, holding under load. So — the other one, last time. Put it on him and mean it.</div></div><p>She levels the emitter at the dock-hand and keys it. The field rolls out full and even and drops over him like weather. He goes slack where he stands, eyes gone, a dead weight held up by nothing — under, and staying under.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That's it. Full field, clean drop. Both weapons live off one good core, and it's good now. You came in here with a dead drain and a dead emitter. You walk out with two.</div></div>
<</if>>
<<set $game_state.current_canvas = "salvage_session_12">>
<<set $game_state.current_node = "salvage_session_12.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_12.n3") === -1) { vn.push("salvage_session_12.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_12_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The dock-hand dresses, pockets his coin, lets himself out into the dark. Kess sets the readout down and doesn't reach for a probe. Nothing left to chase down the seam.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You're done. I mean it — that's the last visit. The drive's back, the drain fires clean, and it held under load without a wobble. Operational.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Whatever they broke in you, it's mended. Get off my cradle and go find out what you can do with it.</div></div>
<<set $game_state.current_canvas = "salvage_session_12">>
<<set $game_state.current_node = "salvage_session_12.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_12.read") === -1) { vn.push("salvage_session_12.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.applyAndNotifyTrait("player", null, "emitter_broken", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle. Whole.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_11_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting — a dockside regular she's used before, paid up front, unbothered. He knows the shape of the job by now. Stands loose against the bench, waiting to be told nothing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's warm today. Warmer than it's been. So don't be careful with it — take him how you want him, top to bottom, all your call. I want the drive under load, not idling.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. I think this one gives you something back.</div></div>
<<set $game_state.current_canvas = "salvage_session_11">>
<<set $game_state.current_node = "salvage_session_11.base">>
<<script>>setup.markCanvasTriggered("salvage_session_11");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_11.base") === -1) { vn.push("salvage_session_11.base"); }<</script>>
<<nobr>>
<<link "Take him." "Canvas_salvage_session_11_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_11_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She puts him down on the cradle and climbs on, sets the pace off her own hips — slow, then flat and hard, exactly the rhythm she wants and not his. "Fine by me," he says, hands off, letting her. Kess watches the meter climb.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That's it. Needle's moving — real charge this time. Don't stop, don't ease it. Run him.</div></div>
<<set $game_state.current_canvas = "salvage_session_11">>
<<set $game_state.current_node = "salvage_session_11.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_11.n2") === -1) { vn.push("salvage_session_11.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_11_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_11_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She wrings him dry and reaches — down inside for the old pull that used to open a man at the base of the spine. This time it catches. It fires clean, full take, and the whole shape of him lays open in her hands.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There it is. Full pull, clean through the finish, no sputter — that's the weapon. That's you. Welcome back.</div></div><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Drain's clean now. Try the other one again — same as before, thumb it at him.</div></div><p>She keys the emitter at the regular. This time a field rolls off it, thin and stuttering. His eyes glass, his breath drags, his knees dip — then it guts out and he catches the bench, swaying, still up on his feet.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There — it drove that time. Weak, half a field, didn't put him down. But the core spilled into the second draw. Can't hold it yet. One more session and it lands full.</div></div>
<</if>>
<<set $game_state.current_canvas = "salvage_session_11">>
<<set $game_state.current_node = "salvage_session_11.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_11.n3") === -1) { vn.push("salvage_session_11.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_11_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The regular dresses and lets himself out, emptied and none the wiser, laid open and already forgetting it. Kess kills the readout and turns it around so Wren can see the line spike and hold.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Drive's seated. Drain's clean. Whatever seized in you, it's turning over on its own now — I don't have to nurse it anymore.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You came in here a wreck with a cold socket. You're walking out a weapon. Go use it on someone who matters.</div></div>
<<set $game_state.current_canvas = "salvage_session_11">>
<<set $game_state.current_node = "salvage_session_11.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_11.read") === -1) { vn.push("salvage_session_11.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle. Armed.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_10_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting — a big one this time, a dock-hand off the heavy cranes, broad and slow-moving. Paid, told nothing. He stands where he's put and says less, patient the way big men are.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's warm now. Last session it caught for a breath — this one it'll catch and hold, I think. So turn it over. Bend him to it, take him deep, work him proper.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. I want to see how much of him you can pull.</div></div>
<<set $game_state.current_canvas = "salvage_session_10">>
<<set $game_state.current_node = "salvage_session_10.base">>
<<script>>setup.markCanvasTriggered("salvage_session_10");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_10.base") === -1) { vn.push("salvage_session_10.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_10_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_10_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She bends him to it — takes him over the cradle, gets herself braced and open, and he feeds his cock into her cunt deep and unhurried, all that weight behind each stroke. "Tell me if it's too much," he says, low. She won't. Kess watches the meter.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Needle's climbing — good, that's the core drinking. Keep him deep, keep the seat full. Don't rush the stroke; the drain wants the whole finish, not a fast one. Let it build.</div></div>
<<set $game_state.current_canvas = "salvage_session_10">>
<<set $game_state.current_node = "salvage_session_10.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_10.n2") === -1) { vn.push("salvage_session_10.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_10_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_10_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes deep, spills up into her cunt, a long shudder of all that weight. She reaches down inside for the old pull, and this time it catches. Grabs.</p><p>The socket floods and takes. The shape of the man comes up her spine near whole — his years, his ledger, his quiet. Most of him, in one long draw.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There — there it is. Strong catch, socket flooding, meter near the top. That's nearly the full take; almost the whole man pulled off one finish. His years, his weight, all of it up the trace. That's the readout I wanted.</div></div><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> While he's still standing — that emitter of yours runs off this same core. If the drain's catching, the other one might wake too. Thumb it at him. Let's see.</div></div><p>She pulls the emitter, levels it at the spent dock-hand, and keys it. Nothing rolls off it — no field, no heat, no give. The big man just blinks at her, waiting. Dead weight in her hand.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Nothing. Figured as much. Core's barely driving the one draw — it's got nothing spare for a second yet. Leave it. It'll come back when the drain's all the way trim.</div></div>
<</if>>
<<set $game_state.current_canvas = "salvage_session_10">>
<<set $game_state.current_node = "salvage_session_10.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_10.n3") === -1) { vn.push("salvage_session_10.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_10_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The big one dresses slow and lets himself out, paid twice and lighter than he came, though he'll never know it. Kess pulls the readout and runs a thumb down the trace, nodding at what he sees.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Drive's nearly back. It caught strong and held near the whole way — that's a working weapon, just short of trim. You're one session from operational. Off, back, and this next one seals it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Come back when you've got coin for the last. One more finish and you walk out of here armed.</div></div>
<<set $game_state.current_canvas = "salvage_session_10">>
<<set $game_state.current_node = "salvage_session_10.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_10.read") === -1) { vn.push("salvage_session_10.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle — one to go.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_9_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting when Wren comes in — a talker, dockside, paid up front and already three sentences deep about the weather, the boats, his cousin's boat. The kind of man who fills a room to keep from thinking.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This one runs his mouth. Good. The drain likes a man who's already leaking — easier to catch the thread and pull it. Core's warmer now. I think it'll hold this time.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> So turn it over, use him however you like — and reach for the take at the finish. If it catches, don't fight it. Let it walk a whole sentence out of him.</div></div>
<<set $game_state.current_canvas = "salvage_session_9">>
<<set $game_state.current_node = "salvage_session_9.base">>
<<script>>setup.markCanvasTriggered("salvage_session_9");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_9.base") === -1) { vn.push("salvage_session_9.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_9_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_9_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She leans back on the cradle and puts him where she wants him, and for once the mouth goes quiet — he works her cunt with his tongue, slow, hands spread on her thighs.</p><p>"Quiet one," he mumbles up at her between passes, half a laugh. "That's fair. Fair." Kess watches the meter, not them.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There — needle's twitching already. He's open. Keep him going. I want him spent when you reach for it.</div></div>
<<set $game_state.current_canvas = "salvage_session_9">>
<<set $game_state.current_node = "salvage_session_9.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_9.n2") === -1) { vn.push("salvage_session_9.n2"); }<</script>>
<<nobr>>
<<link "Take him inside." "Canvas_salvage_session_9_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_9_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She pulls his cock up into her cunt and rides it out, and when he empties she reaches — the old pull, down at the base of his spine — and this time it grabs. Holds.</p><p>A word comes up through him and out his mouth like it was hers to take: "Nobody knows I sank the Marda. I cut her loose myself. For the coin." Then the thread drops.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There it is. Held right through the finish — a whole sentence, clean, true. He didn't even feel it leave. Good. That's the drain working.</div></div>
<<set $game_state.current_canvas = "salvage_session_9">>
<<set $game_state.current_node = "salvage_session_9.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_9.n3") === -1) { vn.push("salvage_session_9.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_9_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The talker dresses, running his mouth again — the weather, the boats — never once wondering why he told a stranger the worst thing he'd ever done. He lets himself out. Kess is already behind her with the probe, reading the seam.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That's the best pull yet. A full sentence, held clean through — the drive's nearly seated. Nearly there.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> One or two more and it'll hold on its own, no coaxing. Come back when you've got coin for the next.</div></div>
<<set $game_state.current_canvas = "salvage_session_9">>
<<set $game_state.current_node = "salvage_session_9.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_9.read") === -1) { vn.push("salvage_session_9.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_8_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a mark waiting — a dockside john she pulled off the coin-boards, shirt already off, sitting back on the bench like he owns the hour. He doesn't. He's meat with a pulse and a day in his head.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's warmer than last time. It'll turn over now — might even bite. Get on top of him. Run it your way, not his. You've done the lying-back work; I want you driving.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. This one I think you catch.</div></div>
<<set $game_state.current_canvas = "salvage_session_8">>
<<set $game_state.current_node = "salvage_session_8.base">>
<<script>>setup.markCanvasTriggered("salvage_session_8");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_8.base") === -1) { vn.push("salvage_session_8.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_8_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_8_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She puts him on his back and climbs on, sets the pace herself — slow, then hard when she wants it hard, hands flat on his chest to keep him down. "Strangest job on the coin-boards," he breathes, half-laughing. She doesn't answer. Kess watches the meter climb.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There it is — needle's live. Keep him going. Don't reach yet. Wait for the finish.</div></div>
<<set $game_state.current_canvas = "salvage_session_8">>
<<set $game_state.current_node = "salvage_session_8.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_8.n2") === -1) { vn.push("salvage_session_8.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_8_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_8_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He empties under her and she reaches — down inside for the old pull, the thing that used to open a man and walk his secrets out. It catches. Weak, a flicker, but it grabs.</p><p>One thread of him comes loose — a wharf name, a crate he moved that morning, the low door behind it. Then it slips and guts out. Gone. But it fired.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> That — there. You felt that? It caught. Only a thread, only a scrap of his day, then it dropped. Doesn't matter. It fired. That's the first real take.</div></div>
<<set $game_state.current_canvas = "salvage_session_8">>
<<set $game_state.current_node = "salvage_session_8.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_8.n3") === -1) { vn.push("salvage_session_8.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_8_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The mark dresses, tosses her a look he can't name, and lets himself out into the dock-dark none the wiser he was read. Kess is already at the meter, thumbing back the spike, reading the shape of the catch.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Weak, yeah. Grabbed a scrap and lost the rest. But hear me — that wasn't a memory of the weapon. That was the weapon. The thing itself, live, doing its work. Everything before this was a dead socket. This wasn't.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It holds longer each time we run it. Costs you every visit. Come back with coin and we'll widen the catch — turn a thread into a whole man.</div></div>
<<set $game_state.current_canvas = "salvage_session_8">>
<<set $game_state.current_node = "salvage_session_8.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_8.read") === -1) { vn.push("salvage_session_8.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_7_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has the next one waiting — a hand off the freighters, weathered, silent, paid up front. He doesn't ask what the job is. Men who work boats that long stop asking.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's warmer than last time. You'll feel it try. It won't hold, but it'll try — and that's the part I need to watch. So turn it over. Use him however you like.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Push it this time. Don't be gentle with it.</div></div>
<<set $game_state.current_canvas = "salvage_session_7">>
<<set $game_state.current_node = "salvage_session_7.base">>
<<script>>setup.markCanvasTriggered("salvage_session_7");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_7.base") === -1) { vn.push("salvage_session_7.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_7_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_7_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He turns her, bends her over the bench, and works into her hard — no talk, no easing in, just a flat steady pound like he's done this his whole life. "Job's a job," he grunts once, and that's the only thing he says. Kess watches the readout, not them.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. Needle's twitching. It knows he's there — it just can't get a grip yet.</div></div>
<<set $game_state.current_canvas = "salvage_session_7">>
<<set $game_state.current_node = "salvage_session_7.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_7.n2") === -1) { vn.push("salvage_session_7.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_7_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_7_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes deep and she reaches — hard, furious, willing the old pull to catch. It flickers. Something at the base of the spine stirs, the weapon coughing awake half a second. She drags at it and it dies anyway. Nothing walks out of him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Did you feel that? It lit. Half a beat, then it dropped. That's not dead — that's a bad connection.</div></div>
<<set $game_state.current_canvas = "salvage_session_7">>
<<set $game_state.current_node = "salvage_session_7.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_7.n3") === -1) { vn.push("salvage_session_7.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_7_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The hand pulls his trousers up and lets himself out, silent as he came in. Kess is already down behind her with the probe, chasing the flicker back along the seam, mouth tight.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You were angry. Good. That's what pushed it over the line far enough to light. Anger's fuel — don't apologize for it, use it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It sputtered because the driver's still weak, not because it's gone. We're close. Two, maybe three more and it holds through the finish. Come back with coin for the next.</div></div>
<<set $game_state.current_canvas = "salvage_session_7">>
<<set $game_state.current_node = "salvage_session_7.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_7.read") === -1) { vn.push("salvage_session_7.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_6_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting when Wren comes in — younger than the others, quiet, first time by the look of him. Sitting too straight, hands flat on his knees, waiting to be told what to do.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This one's green. He'll freeze if you leave it to him, so don't. Take him where you want him — slow to start, then hard. The drain's still faulting, but it caught a flicker last time. I want to see if it does it again.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Same as always. Reach whether it comes or not.</div></div>
<<set $game_state.current_canvas = "salvage_session_6">>
<<set $game_state.current_node = "salvage_session_6.base">>
<<script>>setup.markCanvasTriggered("salvage_session_6");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_6.base") === -1) { vn.push("salvage_session_6.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_6_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_6_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She takes it off his hands — puts him on his back, works him in slow until the shake leaves him, then drops the pace and rides him hard. "Sorry — is that — am I doing it right," he manages, half a question. Kess doesn't look up from the readout.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You're fine. Stop talking. There — needle just twitched. Keep her on him.</div></div>
<<set $game_state.current_canvas = "salvage_session_6">>
<<set $game_state.current_node = "salvage_session_6.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_6.n2") === -1) { vn.push("salvage_session_6.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_6_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_6_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes fast, gasping, hips jerking up into her — and she reaches down inside herself for the old pull. This time something answers.</p><p>A thin thread of it opens at the base of his spine, holds a half-beat, sputters like a wet match — then dies. But it held. Longer than nothing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. You feel that? It caught and ran a half-beat before it dropped. Last time was dead flat. That's not dead anymore.</div></div>
<<set $game_state.current_canvas = "salvage_session_6">>
<<set $game_state.current_node = "salvage_session_6.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_6.n3") === -1) { vn.push("salvage_session_6.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_6_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The boy dresses in a hurry, red to the ears, and lets himself out. Kess pulls a stool up behind Wren and sets the probe to the seam, reading the trace it left.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> The drive's coming back. It held longer that time because it's remembering how — the pathway's still there, just slow to open. Each pass it'll hold a little more before it gives.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Don't chase it hard, you'll tear the seam. Rest, let the strain drop, come back when you've coin for the next. We build it in half-beats.</div></div>
<<set $game_state.current_canvas = "salvage_session_6">>
<<set $game_state.current_node = "salvage_session_6.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_6.read") === -1) { vn.push("salvage_session_6.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_5_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting when Wren comes in — a courier off the fast boats, young, paid, mouthy about it. Jacket half off, grinning like he's already got the story he'll tell later.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This one's a talker. Ignore it. Core's warmer than last time — you might get a flicker off him, might get nothing. That's what I'm here to see. So turn it over, use him however you like.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. Whatever comes up, I want the reading.</div></div>
<<set $game_state.current_canvas = "salvage_session_5">>
<<set $game_state.current_node = "salvage_session_5.base">>
<<script>>setup.markCanvasTriggered("salvage_session_5");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_5.base") === -1) { vn.push("salvage_session_5.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_5_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_5_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The courier's got no patience in him — spins her, bends her over the cradle, works into her hard and fast with his hands dug into her hips. "Best coin on the whole run," he says, breathless, cocky. "Gonna be back for this." Kess watches the meter, not them.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Save your breath, courier. The reading doesn't care how you feel about it.</div></div>
<<set $game_state.current_canvas = "salvage_session_5">>
<<set $game_state.current_node = "salvage_session_5.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_5.n2") === -1) { vn.push("salvage_session_5.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_5_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_5_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes hard inside her and she reaches down for the old pull — and this time something answers. A flicker. The socket warms, half-catches, a fraction of the take opening at the base of his spine.</p><p>Then it drops. Gone before it's a thing. She felt it almost work — worse than nothing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. There — it caught. Half a second, right through the finish, then it dropped. Did you feel that? That's not nothing. That's the first real one.</div></div>
<<set $game_state.current_canvas = "salvage_session_5">>
<<set $game_state.current_node = "salvage_session_5.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_5.n3") === -1) { vn.push("salvage_session_5.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_5_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The courier dresses, still grinning, and lets himself out with a line about coming back. Kess ignores him, already crouched behind Wren with the probe, chasing that half-second flicker down the seam before it goes cold.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It caught and it dropped — that's a driver waking up, not a dead one. The pull's still there. It just can't hold the charge long enough to walk anything out. Yet.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Feeling it half-work is the worst part. I know. But that flicker's the map back. Come back with coin and we chase it — next one, it holds longer.</div></div>
<<set $game_state.current_canvas = "salvage_session_5">>
<<set $game_state.current_node = "salvage_session_5.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_5.read") === -1) { vn.push("salvage_session_5.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_4_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting when Wren comes in — an old regular of his, already stripped, already on the cradle. Hands behind his head. He's done this before and it shows. Bored, easy, patient.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> This one's a regular. Knows the drill, won't spook, won't ask. Core's still cold — you'll pull nothing off him. But I want it turning over so I can read the driver.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> So turn it over. Use him however you like — ride him, finish him. And reach for the take at the finish. Every time. Eyes on the gauge.</div></div>
<<set $game_state.current_canvas = "salvage_session_4">>
<<set $game_state.current_node = "salvage_session_4.base">>
<<script>>setup.markCanvasTriggered("salvage_session_4");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_4.base") === -1) { vn.push("salvage_session_4.base"); }<</script>>
<<nobr>>
<<link "Get on him." "Canvas_salvage_session_4_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_4_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She climbs on and takes him herself — sets the pace, works him at a flat steady grind, nothing performed about it. "Same as always," he says to the ceiling. "Wake me if I'm needed." Kess doesn't look up from the readout.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Keep it steady. I don't need him loud, I need him finishing. Watch the needle when he goes.</div></div>
<<set $game_state.current_canvas = "salvage_session_4">>
<<set $game_state.current_node = "salvage_session_4.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_4.n2") === -1) { vn.push("salvage_session_4.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_4_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_4_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He empties into her and she reaches — down inside for the old pull, the thing that used to crack a man open at the spine and walk his secrets out. Nothing catches. Cold socket, dead as before.</p><p>But the gauge on Kess's readout twitches. Once. A single flick of the needle, then flat.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> There. You see that? Dead through the finish — no charge, no pull — but the needle moved. First time it's moved at all.</div></div>
<<set $game_state.current_canvas = "salvage_session_4">>
<<set $game_state.current_node = "salvage_session_4.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_4.n3") === -1) { vn.push("salvage_session_4.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_4_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The regular climbs off, dresses without a word, lets himself out the way he came. Kess is already crouched at the seam with the probe, chasing that one twitch back down the line, jaw set.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Still dead. Don't mistake me — you pulled nothing, and you won't for a while yet. But that flick? That's the driver taking a breath. It was flat as a stone last time. Now it's not.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> We're getting somewhere. Slow, and it costs you every visit — but somewhere. Come back when you've got coin for the next.</div></div>
<<set $game_state.current_canvas = "salvage_session_4">>
<<set $game_state.current_node = "salvage_session_4.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_4.read") === -1) { vn.push("salvage_session_4.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_3_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has another body waiting — a welder off the night shift, big through the shoulders, paid and told nothing. He stands there rolling his neck, impatient the way big men get when they're being kept from the work they came to do.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's still cold. You'll pull nothing off him — same as last time. But I've got a part to swap while it's turning, so keep it turning. Turn it over, use him however you like.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. I want to watch it fail while I've got my hands in the seam.</div></div>
<<set $game_state.current_canvas = "salvage_session_3">>
<<set $game_state.current_node = "salvage_session_3.base">>
<<script>>setup.markCanvasTriggered("salvage_session_3");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_3.base") === -1) { vn.push("salvage_session_3.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_3_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_3_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>No easing about the welder. He bends her over the bench, gets a hand flat on her back and works into her hard and fast, all shoulder. "Quick job, then," he grunts, not asking. Kess ignores them, elbow-deep in the housing, prying a seized part loose.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Keep it turning. I've got the driver half out — don't stop till the finish.</div></div>
<<set $game_state.current_canvas = "salvage_session_3">>
<<set $game_state.current_node = "salvage_session_3.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_3.n2") === -1) { vn.push("salvage_session_3.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_3_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_3_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes deep and holds her there, spent. She does what Kess said — reaches down inside for the old pull, the thing that used to break a man at the spine. Nothing to reach with. The socket's still cold, still empty.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Dead. Clean through the finish, no charge — and there it is, the part in my hand, seized solid. That's your fault. That's the whole fault.</div></div>
<<set $game_state.current_canvas = "salvage_session_3">>
<<set $game_state.current_node = "salvage_session_3.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_3.n3") === -1) { vn.push("salvage_session_3.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_3_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The welder pulls his coveralls up and lets himself out, paid twice and none the wiser. Kess turns the seized part over under the lamp, thumbing the pitting, almost pleased — the flat pleasure of a man who's finally cornered the thing he was chasing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's the soft driver — the coupling that feeds the drain. It's not broken, it's seized. Been binding for years and you never felt it because you had power to spare. Now you don't, so it reads dead.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Good news is I know what it is now. Bad news is it comes free slow, and it costs you every visit. Come back when you've got coin for the next.</div></div>
<<set $game_state.current_canvas = "salvage_session_3">>
<<set $game_state.current_node = "salvage_session_3.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_3.read") === -1) { vn.push("salvage_session_3.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_2_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has a body waiting when Wren comes in — a rigger off the night boats, paid, told nothing. Boots already off, easy about the wait.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Core's cold. You'll pull nothing off him — I know that. I need it turning over so I can find what's seized. So turn it over. Use him however you like.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time. I want to watch it fail — I need to see where the charge dies.</div></div>
<<set $game_state.current_canvas = "salvage_session_2">>
<<set $game_state.current_node = "salvage_session_2.base">>
<<script>>setup.markCanvasTriggered("salvage_session_2");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_2.base") === -1) { vn.push("salvage_session_2.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_2_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_2_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The rigger lays her back on the cradle, gets her legs up, works into her at a slow flat pace. "Strangest coin I've earned," he says. "Not the worst."</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Quiet. She's not here for you. Pressure's building at the base — I can see it load. That's the driver waking. Keep steady.</div></div>
<<set $game_state.current_canvas = "salvage_session_2">>
<<set $game_state.current_node = "salvage_session_2.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_2.n2") === -1) { vn.push("salvage_session_2.n2"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_2_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_2_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He empties into her. She reaches down inside herself for the old pull — the thing that used to open a man at the base of the spine. Nothing answers. A cold socket where the weapon sat.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Dead. Right through the finish — no charge, no pull, nothing. The socket's there, the driver just won't turn it over. Good. Clean data.</div></div>
<<set $game_state.current_canvas = "salvage_session_2">>
<<set $game_state.current_node = "salvage_session_2.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_2.n3") === -1) { vn.push("salvage_session_2.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_2_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The rigger dresses and lets himself out, paid twice and none the wiser. Kess crouches behind her with a probe, chasing the fault down the seam.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> It's not the drain. Drain's fine. It's the thing that drives it that's gone soft — and that I can bring back. Slow. Costs you every visit. Come back when you've got coin for the next.</div></div>
<<set $game_state.current_canvas = "salvage_session_2">>
<<set $game_state.current_node = "salvage_session_2.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_2.read") === -1) { vn.push("salvage_session_2.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<img src="./videos/scenes/salvage_session_1_t4.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess has the first one waiting when Wren comes in — a dock hand off the cargo runs, paid, told nothing. Big through the shoulders, half-stripped, easy about the coin and the quiet.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> First body. Don't expect anything off him — the take's dead, I know it's dead. But I can't read a drain that isn't turning over, and yours has been cold since they hauled you in. So turn it over. Use him however you like — rough, slow, doesn't matter to me, only to the readout.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And reach for the take at the finish. Every time, hard as you can. I want to watch exactly how it fails — the shape of the failure tells me what's left to fix.</div></div>
<<set $game_state.current_canvas = "salvage_session_1">>
<<set $game_state.current_node = "salvage_session_1.base">>
<<script>>setup.markCanvasTriggered("salvage_session_1");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_1.base") === -1) { vn.push("salvage_session_1.base"); }<</script>>
<<nobr>>
<<link "Get to it." "Canvas_salvage_session_1_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_1_fuck_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He bends her over the lip of the cradle, one hand flat on her back, and works into her from behind at a steady loading-dock pace. "Cleanest coin I've made all season," he says to no one.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Save your breath for the work. She's a bench, not a date. Steady pace — good, I can read steady. Watch her spine, right at the base, where the seam sits. That's the only part of her I care about.</div></div>
<<set $game_state.current_canvas = "salvage_session_1">>
<<set $game_state.current_node = "salvage_session_1.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_1.n2") === -1) { vn.push("salvage_session_1.n2"); }<</script>>
<<nobr>>
<<link "Keep going." "Canvas_salvage_session_1_Node_leak">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>Somewhere in the steady of it her spine trips — not arousal, a fault. Something older loads behind her eyes with no place to put it. Smoke. Not this dry-dock's oil-smoke. The kind that doesn't wash out of a person.</p><p>A bigger hand than the dock hand's. It doesn't hunt for the seam — it goes straight to the socket at the base of her spine, like it set the socket there, like it knew the map before there was a map.</p><p>A voice says a word over her — not the kind said over a thing someone owns. The kind said over a person. And under it, off the edge of hearing, one syllable of a name. It fits her like a socket she never knew was empty.</p><p>She reaches for the rest of it — reaches the way Kess told her to reach for the take — and it goes out like a held breath let go. Gone. Just the dock hand's hands again, and the cold.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> ...who.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> You said something. Don't chase it — whatever that was, it's not on my readout, so it's not the job. The name, the smoke, none of it. Reach for the take instead. Now, while he's close.</div></div>
<<set $game_state.current_canvas = "salvage_session_1">>
<<set $game_state.current_node = "salvage_session_1.leak">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_1.leak") === -1) { vn.push("salvage_session_1.leak"); }<</script>>
<<nobr>>
<<link "Reach for the take." "Canvas_salvage_session_1_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/salvage_session_1_finish_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He empties into her and she does what Kess said — reaches down inside herself for the old pull, the thing that used to open a man at the base of the spine and walk his secrets into her hands. Reaches, and closes on nothing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Dead. Right through the finish — no charge, no pull, flat line the whole way. A cold empty socket where the weapon used to sit. Good. That's clean data, though — the first clean thing I've got off you.</div></div>
<<set $game_state.current_canvas = "salvage_session_1">>
<<set $game_state.current_node = "salvage_session_1.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_1.n3") === -1) { vn.push("salvage_session_1.n3"); }<</script>>
<<nobr>>
<<link "Let him go." "Canvas_salvage_session_1_Node_read">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The dock hand dresses, pockets the second half of his coin, and lets himself out none the wiser. Kess crouches behind her with the probe, tracing the drain-seam by feel — and stops, thumb resting on the ridge of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Huh. This seam was re-seated. By hand. Someone had their fingers in here before me — and they didn't have to look for the socket, they went straight to it. No field-medic did this. A field-medic gropes around for it. Whoever closed you up already knew exactly where it lived.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Anyway. It's not the drain — drain's sound. It's the thing that drives the drain that's gone soft, and that I can bring back. Slow. Costs you every visit. Come back when you've got coin for the next one.</div></div>
<<set $game_state.current_canvas = "salvage_session_1">>
<<set $game_state.current_node = "salvage_session_1.read">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_session_1.read") === -1) { vn.push("salvage_session_1.read"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", -8.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_session", "add", 1.0, true, null);setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 0.0, true, null);setup.showEffectNotification();<</script>>
[[Off the cradle.->Location_kess_berth]]<p>The torch is cold on its hook and the cradle sits empty, the hull up on its blocks going nowhere. Kess keeps hours like any other working man — ten in the morning to ten at night, and not a minute either side. The black water under the walkway doesn't move. Come back when he's cutting.</p>
<<set $game_state.current_canvas = "berth_offhours">>
<<set $game_state.current_node = "berth_offhours.base">>
<<script>>setup.markCanvasTriggered("berth_offhours");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("berth_offhours.base") === -1) { vn.push("berth_offhours.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Back up the ramp.->Location_underworld_strip]]<img src="./videos/locations/kess_berth.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Kess is where he always is, up in the guts of something with a torch. He doesn't look round when Wren comes down the ramp — just tips his chin at the cradle, and at the coin-box bolted to the bench beside it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Box first, then the cradle. Same as always. No coin, no session — go make some and come back.</div></div>
<<set $game_state.current_canvas = "salvage_repair_hub">>
<<set $game_state.current_node = "salvage_repair_hub.base">>
<<script>>setup.markCanvasTriggered("salvage_repair_hub");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_repair_hub.base") === -1) { vn.push("salvage_repair_hub.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 10}])>>
<<link "Pay the box and get on the cradle. (10 coin)" "Location_kess_berth">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "repair_armed", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -10, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Pay the box and get on the cradle. (10 coin)">Pay the box and get on the cradle. (10 coin) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 10}])>>)</span><br>
<</if>>
<<link "Not today." "Location_underworld_strip">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><img src="./videos/scenes/kess_berth_intro.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The berth's colder than the strip, all cut steel and sparks, the red light of the underground shut out behind a hull. Someone's up on the work-cradle with a torch. It cuts out when Wren's boots hit the floor.</p><div class="dialog-block dialog-npc"><div class="dialog-content"><strong>Stranger:</strong> Yard's shut. Turn around.</div></div><span id="cascade-b3-beat-0"><div class="dialog-block dialog-npc"><div class="dialog-content"><strong>Stranger:</strong> ...that's Vance plate under the coat. No. I don't touch company work — company work touches back. Out.</div></div><p>He's already turning back to the hull, done with it.</p><span id="cascade-b3-beat-1"><<linkreplace "Name the fault.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I can't operate. Something in the core.</div></div><p>The word core turns the man's head. He comes down off the cradle, a gauge already in his hand, and holds it to the base of Wren's spine.</p><span id="cascade-b3-beat-2"><<linkreplace "Let him read it.">><div class="dialog-block dialog-npc"><div class="dialog-content"><strong>Stranger:</strong> ...that's not a Vance core. That's not anybody's core I've got a book for. Bespoke. Hand-built — somebody made you one at a time.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> I'm Kess. Sit down.</div></div><span id="cascade-b3-beat-3"><<linkreplace "Hear the terms.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Here's how it runs. I don't fix a thing like you in one sitting. I open you, work one thing, make it hold, close you up — then you come back and we go again. Session at a time, however many it takes.</div></div><span id="cascade-b3-beat-4"><<linkreplace "Ask the price.">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> What's it cost.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> Coin. Not company scrip — I can't spend Vance money without Vance noticing, and neither can you. Ten a session, and you're on the cradle.</div></div><span id="cascade-b3-beat-5"><<linkreplace "Hear the catch.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/kess.jpg" alt="Kess" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Kess:</strong> And it's up front — every session, before I open you. You're broke, I can read that off you too, so you'll be earning it up the strip between visits — the House, the Pit, whoever's paying. No coin, no session. That part's yours.</div></div>[[So that's the deal.->Location_kess_berth]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "salvage_kess_intro">>
<<set $game_state.current_node = "salvage_kess_intro.base">>
<<script>>setup.markCanvasTriggered("salvage_kess_intro");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_kess_intro.base") === -1) { vn.push("salvage_kess_intro.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "kess_met", "set");<</script>><img src="./videos/scenes/salvage_waterfront_dawn.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The phone in her hip buzzes before the light's full up. Mercer's summons — the pattern she answers on reflex, before she's decided anything. She gets her feet under her to go in and report. The bad leg folds and puts her down on one knee.</p><span id="cascade-b2-beat-0"><p>The other road, then. Renner's depot, cheap hands, the cover she can still wear. She reaches back for the drain to check it's seated, and it answers cold — a dead click where the charge should catch. Nothing takes.</p><span id="cascade-b2-beat-1"><<linkreplace "Take stock.">><p>Both roads want a body that works. Hers doesn't. The leg holds if she never asks it to turn; the drain seats but won't fire; the core sits wrong behind her sternum, a low fault she can feel the edge of and can't reach.</p><span id="cascade-b2-beat-2"><<linkreplace "Consider the company.">><p>The company fixes assets. She knows exactly what fixing is: the techs open her up on the bench, find the fault, and take her back to spec.</p><span id="cascade-b2-beat-3"><<linkreplace "Count what that costs.">><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Back to spec is back to nothing. The four words go. The name that fit a lock she didn't know she had — filed, overwritten, gone.</div></div><p>And she finds she does not want that. She does not look at why.</p><span id="cascade-b2-beat-4"><<linkreplace "Let it ring.">><p>So she doesn't call it in. The summons rings out unanswered — the first order in her life she's let go dark. No one's watching. No one will know until it's already done.</p><span id="cascade-b2-beat-5"><<linkreplace "Go off the books.">><p>There's underworld talk of hardware worked off the books: a ship-breaker down past the gate, in the underground, who takes machines the company can't see and asks the wrong questions for the right coin. The berth's down there, off the strip. She goes to find it.</p>[[Go find it.->Location_the_waterfront]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "salvage_body_wont_hold">>
<<set $game_state.current_node = "salvage_body_wont_hold.base">>
<<script>>setup.markCanvasTriggered("salvage_body_wont_hold");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("salvage_body_wont_hold.base") === -1) { vn.push("salvage_body_wont_hold.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "salvage_entered", "set");<</script>><p>She comes back a little at a time, into a room that has changed. The men are gone. The near door stands open on an empty corridor. The frame she was strapped to is on its side where somebody knocked it going out fast. She is on the floor by the grate they meant to hose her over, and her left leg will not answer, and the light under her skin is out — properly out, not flickering, dark the way a cooled coal is dark.</p><p>Through the wall, two men are arguing.</p><p>One of them is Bastien. She would know that voice anywhere now, and it has not changed — flat, unhurried, reasonable, a man explaining a position he sees no reason to raise his voice about. The other voice she has never heard in her life, and it goes through her wrong, because it is furious, and the fury has something under it she has no file for. It takes her a long muddled moment to find the word for the thing under it, and the word she finds is grief, and that makes no sense at all, because nobody has ever once been angry on her account, let alone torn up about it.</p><p>She is too far gone to hold any of it. The words come apart before they reach her. Four get through the whole of it and no more. One is Bastien's, and it is a word she knows the shape of because it is a word men use about her: an ownership word, said the way you'd say the price of a thing. And three come across in the other voice, and one of those three is a name — she is nearly certain it is a name — and it goes into her somewhere below the amputation and fits, the way a key fits the one lock in a house that was left, a lock she did not know she had, and she cannot for her life say what the name was or why a stranger saying it should make the empty places in her ache like frostbite coming warm. And the last word, in that same broken voice, is one that has no business anywhere near a room like this, a word you say over a grave and not over a thing you are haggling for. She reaches for all four of them and they slide out of her hands.</p><p>The arguing stops. Not trails off — stops, the way it stops when one man has said the thing there is no answer to.</p><p>Then the door — the far one, off its hinges already — and he comes in, and she cannot make him hold still. He is a shape against the bad light and a smell of old smoke, and he crosses the room fast and goes down on one knee beside her, and she flinches, because everyone who has knelt beside her in this room has done it to use her. He doesn't. He turns her, careful of the dead leg, the way you'd turn something you already knew the weight of, and he finds the seam at the base of her spine without looking for it — without searching, his hand going straight to it in the dark like a hand going to its own pocket — and he puts the weight back.</p><p>The drain seats home. She feels it take, feels the terrible backed-up pressure behind her sternum find, at last, the door it has been beating on, and begin — slowly, agonisingly — to bleed off down a channel that is hers again. It is not relief. It is too late for relief. But it is the difference between a thing that is failing and a thing that is going to fail, and her leg, somewhere very far away, twitches once and remembers most of itself.</p><p>He is already up. Whatever he came to do, it was not to talk to her; or he cannot; or there is no time. He puts two fingers against the side of her face for exactly as long as it takes to be sure she has come back, and even that is not a stranger's touch, and then he is a shape in the doorway and then he is gone, and she is alone in a broken room with her weapon back in her spine and no idea on God's earth why any of it just happened.</p><p>She wakes on the waterfront at dawn. Sore, and not whole — the fault sits in her core where it stopped and does not move, and she already knows, the way you know a scar before you've seen it, that it is not going to move again. But the drain is in its socket. Her leg carries her. The city is exactly where she left it, indifferent, the gulls going and the tide out, Mercer's work waiting and Renner's depot waiting and Sunday coming around again the way Sunday does.</p><p>And four words she cannot hold, and cannot stop reaching for, and one of them a name that fit a lock she didn't know was hers.</p>
<<set $game_state.current_canvas = "captivity_cain">>
<<set $game_state.current_node = "captivity_cain.base">>
<<script>>setup.markCanvasTriggered("captivity_cain");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("captivity_cain.base") === -1) { vn.push("captivity_cain.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 1.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "emitter_broken", "set", 1.0, false, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "captivity_done", "set");<</script>>
[[Get up off the dock.->Location_the_waterfront]]<p>It doesn't happen the way she has been bracing for. There is no tearing, nothing gives, no part of her comes off in anyone's hand. What happens is quieter and much worse: the charge fills the last of the room it has, and then it keeps coming, and there is simply nowhere left for it to be.</p><p>The man in her feels it before she does — feels the whole housing of her go rigid under him, feels the hum climb past anything a body makes — and he is already scrambling back off the frame when the light goes. Not out. Up. Every seam of her, every port and plate and the long empty line of the socket Bastien stripped, all of it comes up white at once through the skin, so that for a moment she is lit from the inside like a room with the lamp knocked over, and the men are on their feet and against the walls and one of them is saying oh Christ oh Christ and none of it reaches her.</p><p>Because inside, where she has spent her whole life being quiet, it has finally gone loud. Every door she was built with is open at once. Everything the company filed away where she couldn't reach it is standing in the light — not memories, she has no memories, only the shape of the places they were cut out of, a hundred hollows all shrieking the same note. She has been a thing that feels nothing but the sex it's used for. She feels this. She feels all of it, for one white unsurvivable second, the entire amputated size of herself, and there is no word in her for what it is except that it is hers and they took it.</p><p>Then the safeties she doesn't own throw themselves, the way a station drops its own breakers before the fire, and it all goes black from the top down. The light under her skin dies. The hum falls out of the air. Her held-open eyes lose the ceiling.</p><p>The last thing that reaches her, from very far away, is the sound of the door — not the men's door, the far one, the one that has never once opened while she was awake — coming off its hinges.</p>
<<set $game_state.current_canvas = "captivity_break">>
<<set $game_state.current_node = "captivity_break.base">>
<<script>>setup.markCanvasTriggered("captivity_break");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("captivity_break.base") === -1) { vn.push("captivity_break.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "captivity_broken", "set");<</script>>
[[Go dark.->Location_captive_room]]<video src="./videos/sex/cell_he_does_not_stop_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>There's a line of them and it hasn't thinned. Someone's got her by the hips on the concrete and is working into her cunt at a flat, bored pace, and the man before him left her open and slick enough that it goes easy. That isn't the part they've stopped watching for. Under her sternum the plate has started to stutter — light coming up between her ribs in pulses, off-time with the fucking, a dull red that wasn't there when they started and won't settle back down. Every load one of them put in her ticked it higher. It has nowhere to go. She's lit from the inside like something left switched on.</p><p>The socket at the base of her spine sits empty — a dry seam in the metal where the drain used to seat. That was the part that took. Stripped out, there's only the filling: cock after cock stacking charge behind the plate with no line out, and the read-out on her chest climbs while the man fucking her doesn't notice, chasing his own thing, watching her tits move with it. The glow throbs up out of her skin and nobody backs off. They just wait their turn and fill her hotter.</p><p>'The fuck is she doing, she's lighting up.'</p><p>'Who cares, she's still tight — go, go, you're up.'</p>
<<set $game_state.current_canvas = "cell_use_he_does_not_stop">>
<<set $game_state.current_node = "cell_use_he_does_not_stop.base">>
<<script>>setup.markCanvasTriggered("cell_use_he_does_not_stop");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_he_does_not_stop.base") === -1) { vn.push("cell_use_he_does_not_stop.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_he_does_not_stop_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_he_does_not_stop_a_crew_man_balks_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The one in her cunt pulls out mid-stroke. Not finished — he just steps back off her, cock still hard and shining, staring at the light going through her ribs like it's finally reached him what he's been putting himself into. She keeps twitching where he left her, hips still tipped up because nobody told them to come down. The glow pulses again, brighter, and something under the plate makes a sound — a thin electric whine climbing pitch — and it's the machine, not her, because her face hasn't changed at all. Her face stopped changing a while back.</p><p>He backs another step and won't go near her again, cock going soft in his fist, looking around at the others like one of them's going to explain it. None of them do. The whine keeps rising out of her chest and the red bar under her sternum flickers, gutters, catches — a light with a short in it — and still she lies open on the floor the exact way the last man left her, ass up, thighs slack, waiting to be used by whoever's next.</p><p>'Boss. Boss, that's — that's not right.'</p><p>'Whatever the fuck she is, that ain't it working. That's it breaking.'</p><p>'She's glowing, man, look at her chest, I'm not putting my dick back in that.'</p>
<<set $game_state.current_canvas = "cell_use_he_does_not_stop">>
<<set $game_state.current_node = "cell_use_he_does_not_stop.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_he_does_not_stop.n2") === -1) { vn.push("cell_use_he_does_not_stop.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_he_does_not_stop_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>Bastien crosses the floor unhurried and crouches at her chest to look — not at her face, at the read-out, at the number climbing behind the flicker. He watches it the way he watches everything about her: like a figure going into a ledger. He lays two fingers flat on the plate, feels the heat coming off it, the whine buzzing up through the metal into his hand, and he doesn't pull them away. He turns his head and looks at the man who stepped back, then at the rest of them standing there with their cocks out, gone uncertain.</p><p>Then he takes his fingers off the plate, wipes the heat off on her tit, and stands, and tells them to get back in her. He's not curious out loud. He's curious in the eyes — but his mouth is only cruel, and the two don't argue with each other in him.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'It's not breaking. It's filling. There's a difference and I've been watching it fill for a while now.'</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'You fuck her, it charges her — and somebody pulled the one part off her that let her spend it. So now it just goes in. Stacks up. Nowhere out. That's what the light is.'</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'Which means if she comes apart, I finally get to see what they built her out of. And there's exactly one way to reach that number. So finish. All of you. Back in her.'</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'She doesn't get to keep it, and she doesn't get to hold anything back either. Fill the filthy machine till it can't take another drop.'</div></div><p>'You heard him. Turn her over.'</p>
<<set $game_state.current_canvas = "cell_use_he_does_not_stop">>
<<set $game_state.current_node = "cell_use_he_does_not_stop.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_he_does_not_stop.n3") === -1) { vn.push("cell_use_he_does_not_stop.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_he_does_not_stop_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_he_does_not_stop_used_past_failure_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>They turn her over. Someone hooks her by the ankle and folds her onto her front, and she goes where she's put — arms loose under her, cheek flat to the concrete — and then there's weight across her back and someone's in her ass now, driving hard the second the boss said go. Her legs stay where they set them, knees shoved apart, ass tipped up, because she's stopped bracing, stopped everything that isn't the plate. And it's her ass — the socket right there above the crack, dead and empty, the drain that used to take pulled out of it. Nothing takes now. Every thrust into her only feeds the thing that fills. The red bar between her ribs has stopped pulsing and gone solid, a level rising, and each grunt and each load somebody empties into her walks it up toward the top.</p><p>One of them reaches under her and works her clit rough while the man behind fucks her, because the boss said the fast way is to make her come — and when it wrenches through her the read-out jumps three marks at once and something under the plate flares white. It isn't pleasure on her at that point. It's overflow with no drain, and it burns; the charge climbing through her with nowhere to be, cooking her from the plate out. She makes a sound she hasn't made — thin, mechanical, wrong — and they laugh and hold her down and drag it out of her again.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'Make her come. That's what moves it. Anyone can just fuck her — I want her spiking.'</div></div><p>'There it goes — you see it jump? Do it again, rub her out.'</p><p>'Hold her, hold her, she's going, she's fucking going—'</p><p>'Look at that light, it don't stop where the last one did.'</p>
<<set $game_state.current_canvas = "cell_use_he_does_not_stop">>
<<set $game_state.current_node = "cell_use_he_does_not_stop.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_he_does_not_stop.n4") === -1) { vn.push("cell_use_he_does_not_stop.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_he_does_not_stop_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/scenes/cell_he_does_not_stop_the_read_out_the_break_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The read-out reaches the top of its climb and stops — not because they've stopped, but because there is no higher left for it to go. The bar under her sternum is full end to end, a hard flat line of light burning through the skin between her ribs, too bright now to look at straight. The whine has gone past sound into a pressure you feel in your teeth. Everywhere else she's dark: face slack, eyes open on nothing, body still moving only because the man behind her is still moving it, hips still working her onto him out of habit and weight. Every small resistance the machine ever had — gone at once, given up together.</p><p>Bastien stands over the top of her head watching the plate, not her, the way he has the whole time. Charged to the top. No socket. No out. Something in her has to give with all that light and nowhere for it, and under the flat glare of the full read-out — while a man is still emptying into her ass and doesn't even notice — it does.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> 'There. Top of the gauge. Now we find out what they were hiding in you.'</div></div><p>'Boss — boss, it's not moving anymore, it's just—'</p>
<<set $game_state.current_canvas = "cell_use_he_does_not_stop">>
<<set $game_state.current_node = "cell_use_he_does_not_stop.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_he_does_not_stop.readout") === -1) { vn.push("cell_use_he_does_not_stop.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -14.0, true, null);setup.showEffectNotification();<</script>>
[[Take the last of it.->Location_captive_room]]<video src="./videos/sex/cell_stutter_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The mattress lost its sheet a long stretch ago. She's face-down on the bare ticking, hips hauled up, and the man behind her has both hands clamped on her waist, fucking into her cunt at a flat bored pace — the pace of a thing he's used long enough to stop looking at. He isn't working for it. He's just emptying himself into a hole that's been left open for him.</p><p>Under her sternum the plate that used to burn clean white sits dim now — a dull ember guttering through her skin with every shove, the charge stacked so high behind it that it's stopped glowing right. Each time his cock bottoms out, the read-out over the plate strobes and climbs another notch. Nobody in the room is bothering to read it yet. They will.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Look at it take that. Filthy machine doesn't even need telling anymore — just lies there and lets itself get bred.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Barely lit. Whole thing's running on fumes. Good.</div></div>
<<set $game_state.current_canvas = "cell_use_stutter">>
<<set $game_state.current_node = "cell_use_stutter.base">>
<<script>>setup.markCanvasTriggered("cell_use_stutter");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_stutter.base") === -1) { vn.push("cell_use_stutter.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_stutter_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_stutter_her_leg_dies_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>It goes between one thrust and the next. Her left leg — braced, taking her weight on the knee — simply stops. The muscle lets go all at once, the joint unlocks, and the leg slides out flat across the ticking, dead weight, slack from hip to heel. No twitch. No shudder back to life. It just quits.</p><p>Her hip drops crooked with nothing under it holding the line, and the man's next stroke shoves her sideways across the mattress because half of what used to brace her isn't answering anymore. Somewhere under the skin a thing that was supposed to keep working has gone dark and stayed dark, and the rest of her body keeps getting fucked around the failure like it never happened.</p>
<<set $game_state.current_canvas = "cell_use_stutter">>
<<set $game_state.current_node = "cell_use_stutter.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_stutter.n2") === -1) { vn.push("cell_use_stutter.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_stutter_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_stutter_he_keeps_going_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He doesn't stop. He barely slows — just reaches down, catches the dead leg under the knee, and hooks it up over his forearm to get the crooked hip back where he wants it. Then he fucks her exactly as before, harder if anything, the slack limb swinging loose off his arm with every drive of his cock like a thing that's already been thrown out.</p><p>The rest of the room watches a machine's leg give out and does the only arithmetic that matters to them: it's still a hole, and the hole still works. Nobody moves to stop it. Nobody would. They lean in closer instead, to see.</p><p>"Its fuckin' leg just died — keep going, keep going, don't you dare stop!"</p><p>"Leg's dead, hole's not. Who cares."</p><p>"Ride it till the next bit falls off."</p>
<<set $game_state.current_canvas = "cell_use_stutter">>
<<set $game_state.current_node = "cell_use_stutter.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_stutter.n3") === -1) { vn.push("cell_use_stutter.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_stutter_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/scenes/cell_stutter_bastien_leans_in_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Bastien comes off the wall and crouches at the edge of the mattress, close, elbows on his knees, watching the dead leg swing where it hangs off the man's arm. Not her face — her face has never been the thing. He watches the failure. He reaches out and pushes two fingers deep into the slack meat of her thigh, testing, feeling for a tone that isn't there anymore, and something moves behind his eyes that has nothing to do with mercy.</p><p>He's interested. He's always most interested when a part of her stops. He keeps his fingers buried in the dead muscle a moment longer than he needs to, thumb pressing, reading her by hand while the man's cock keeps working her hole two inches away.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it goes. Left side's dropping out — right on the number I said it would.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Don't you fucking stop. I want to see what quits next.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Look at that. Bought a machine, and it's coming apart while it gets used. Beautiful.</div></div>
<<set $game_state.current_canvas = "cell_use_stutter">>
<<set $game_state.current_node = "cell_use_stutter.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_stutter.n4") === -1) { vn.push("cell_use_stutter.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_stutter_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/scenes/cell_stutter_the_read_out_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>On the sternum plate the read-out has climbed again — one more increment, logged the second the man's pace picked back up. Every stroke into her stacks a little more charge behind a plate that has nowhere left to send it. The socket at the base of her spine, where the drain used to bleed it off — where anal used to take something out of a man instead of pour it into her — sits empty and cold, a dead port in the small of her back that nothing plugs anymore.</p><p>The number does not fall. It has never once fallen. Bastien's eyes go straight to it and stay there — not on the swinging leg, not on the crew, not on her — on the count. He takes out the little book and writes the new figure under all the others, unhurried, while behind the number the plate flickers like it's finally getting tired.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Higher. Every time it fails, the number still climbs. That's the part they never believe.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Keep filling it. It's got nowhere to put any of it.</div></div>
<<set $game_state.current_canvas = "cell_use_stutter">>
<<set $game_state.current_node = "cell_use_stutter.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_stutter.readout") === -1) { vn.push("cell_use_stutter.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -14.0, true, null);setup.showEffectNotification();<</script>>
[[Stay where she's dropped.->Location_captive_room]]<video src="./videos/sex/cell_overflow_legs_held_open_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The crew are already in the room when it starts. No unwrapping, no chair pulled up slow — that part's over, she's been stripped since before they came in. Two of them take a leg each and fold her open on the mattress, a knee pinned to the concrete on either side, a forearm braced across each thigh, a hand clamped on each ankle. She's spread and she's held and nothing's inside her yet.</p><p>That's the point of holding her first. What they're about to do works better if she can't close on it, so they get her open and locked before a single cock touches her. The plate under her sternum reads high enough today to be worth the trouble, and every man in the room knows what a high reading means: she's easy to push over now, and pushing her over is the whole job.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Hold her open. I want the reading clean when she goes.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Look at it — a machine that fills up like a cheap whore and can't even spill what's in it. Bought and paid for, and it doesn't even work.</div></div><p>"Get the legs — spread the filthy thing wide."</p><p>"It can't even close, look at it, it's just held there."</p><p>"Stay still, hole. You're gonna want to be real still for this."</p>
<<set $game_state.current_canvas = "cell_use_overflow">>
<<set $game_state.current_node = "cell_use_overflow.base">>
<<script>>setup.markCanvasTriggered("cell_use_overflow");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_overflow.base") === -1) { vn.push("cell_use_overflow.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_overflow_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_overflow_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>A man kneels into the gap they've made her hold and pushes into her cunt, and he doesn't fuck her so much as work her — a thumb set on her clit, a rhythm picked cold, not to get himself off but to get her off, because that's the order he was given. They're not here to come in her. They're here to make her come. A forced orgasm spikes the read-out harder than anything else they can do to a body like hers, and every man in the room has learned that now, and they're grinning at it.</p><p>She holds against it because there is nothing else left to do with her hands full and her legs locked, and it doesn't matter. The machine comes when it's made to. He works her over the edge with the flat patience of a man doing paid work, and behind her sternum something starts to climb toward the wall it always finds now.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Make her spill. Doesn't matter what the thing wants — a machine comes when it's made to, and the number jumps every time it does.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. Force it out of her. That reading's worth twice what just fucking her is — so make the filthy thing come, and mind you get it all the way.</div></div><p>"Come on, machine, give it up."</p><p>"Look at it fight — it doesn't want to, that's the best part."</p><p>"Doesn't matter what it wants, does it. Push it over."</p>
<<set $game_state.current_canvas = "cell_use_overflow">>
<<set $game_state.current_node = "cell_use_overflow.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_overflow.n2") === -1) { vn.push("cell_use_overflow.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_overflow_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_overflow_it_hurts_the_overflow_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>And she comes, and it's wrong. The charge goes off behind her sternum and slams into a wall that a body built right doesn't have — the heat that used to run out through the drain at the base of her spine has nowhere to run, so it turns and runs back up into her chest and stacks on top of the last load, and the coming stops being pleasure halfway through and becomes a fault firing down a line. She screams. Not the noise a whore makes on cue — a real one, torn out of her, the sound of a thing overloading with the vents shut.</p><p>The crew think it's the funniest thing they've seen all week. She screamed coming, and it clearly hurt her, and that just makes them want another one out of her. Someone's laughing hard enough to lean on the wall.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That's the overflow. No channel — it's got nowhere to put itself, so it puts it in her. Scream all you want, filthy thing. The number doesn't care, and I paid for the number.</div></div><p>"Ha — hear that? It hurts it! It hurts the machine to come!"</p><p>"It's crying, look — do it again, make it scream again."</p><p>"Whores don't scream when they come, sweetheart. You're broke."</p><p>"Doesn't even work right and we're still using it."</p>
<<set $game_state.current_canvas = "cell_use_overflow">>
<<set $game_state.current_node = "cell_use_overflow.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_overflow.n3") === -1) { vn.push("cell_use_overflow.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_overflow_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_overflow_they_keep_going_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>It changes nothing. They take turns on her. One pulls out and the next kneels straight in, her cunt then her ass by turns, and every load that goes into her adds a step to the thing stacked behind her sternum and takes nothing back out. The socket at the base of her spine sits empty through all of it — the one thing that ever drew any of this back out of her, stripped clean and gone — so her ass isn't a drain any more, it's just another hole that fills her. It only ever adds now. Nothing leaves.</p><p>Bastien has dragged his chair up close. He is not watching her face, or her cunt, or the man buried in her. He is watching the plate under her sternum, where the light has stopped sitting steady and started to climb and hold, and he is writing the number down in a book propped on his knee, checking it against her chest between loads the way a man reads a gauge that's running hot.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Keep her going. It stacks — nothing comes back out of a machine with its drain pulled, so pour it all in.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Filthy overfilled thing. You'll take every one of them and hold every drop, and the reading goes up each time. That's all you're for now.</div></div><p>"Next — get in the hole, it can't spill."</p><p>"Fill it right up, it's got nowhere to put it."</p><p>"Passed round like it's nothing, this one — and it takes it every time."</p>
<<set $game_state.current_canvas = "cell_use_overflow">>
<<set $game_state.current_node = "cell_use_overflow.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_overflow.n4") === -1) { vn.push("cell_use_overflow.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_overflow_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>When they finally leave her spread and slack on the mattress, the plate under her sternum is not doing what it did before they started. The light in it has stopped being steady and gone to a pulse, higher than she has ever seen it read, and it does not come down. It sits there, lit, climbing on nothing, with no drain to bleed it off.</p><p>Bastien crouches over her and looks at it — only at it. He reads the number, writes the number, and the number is the whole reason he came. It went up. It does not go down. Out of everything that was done to her in this room, that is the one thing that changed, and it is the only thing in here that he counts as changed at all.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There's the reading. Higher than the last. You're closer now, machine — and every hole they put in you drives it up another notch and it never once falls.</div></div>
<<set $game_state.current_canvas = "cell_use_overflow">>
<<set $game_state.current_node = "cell_use_overflow.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_overflow.readout") === -1) { vn.push("cell_use_overflow.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -14.0, true, null);setup.showEffectNotification();<</script>>
[[Come down off it.->Location_captive_room]]<video src="./videos/sex/cell_frame_strapped_into_the_rig_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The frame is bolted to the floor before she's brought to it — a waist-high steel jig with a padded bar and cuffs at the four corners, built to hold something down while it gets worked on. They don't wrestle her into it. They set her. Fold her over the bar, feed her wrists into the cuffs, wrench the strap across the small of her back and crank it until she's fixed face-down at the exact angle they want, ass up, knees spread by the frame's own geometry, the empty socket at the base of her spine sitting open to the room.</p><p>Not held down. Positioned. Locked to spec, like a part dropped into a machine that's about to run — no hands needed once the straps bite, because a fixture doesn't get to move. Someone tests it with a shove and she doesn't shift an inch. The frame takes it for her.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Bolt it down. I don't want the machine wandering off while it does its job.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Ass up, hole open, spine to the ceiling. That's the only shape it's good for now — so lock it there and leave it.</div></div>
<<set $game_state.current_canvas = "cell_use_frame">>
<<set $game_state.current_node = "cell_use_frame.base">>
<<script>>setup.markCanvasTriggered("cell_use_frame");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_frame.base") === -1) { vn.push("cell_use_frame.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_frame_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_frame_positioned_tuned_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>A crank on the side of the frame lifts the bar an inch; a boot hooks her knee and drags it wider until the cunt and the empty ass sit at the height a standing man wants. A fist takes her hair and tips her head off the frame to clear the throat, lets it drop, tips it again — checking the line. They talk over her the whole time, the way men talk over a bench they're setting up. Not to her. About her.</p><p>Someone works two fingers into her ass and spreads, eyes the angle, wipes the hand off on her thigh. Someone else thumbs her cunt open, lets it close, satisfied. Nobody asks if she's ready because ready isn't a setting the rig has. Tuned. Zeroed. Set to spec and left waiting at the height of the line.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Zero the readout before anyone touches it. I want a clean count off this run — every load logged, none of it wasted.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Height's good. Now it's just a hole at the right elevation. Start the line.</div></div><p>Bring her up an inch — I'm not squatting for a machine.</p><p>There. That's the height. Lock it.</p><p>Spread's good. Both holes on the same level, take your pick.</p><p>Wipe your hand on it, it's not gonna complain.</p>
<<set $game_state.current_canvas = "cell_use_frame">>
<<set $game_state.current_node = "cell_use_frame.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_frame.n2") === -1) { vn.push("cell_use_frame.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_frame_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_frame_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Then they run her. One after another down the line — cunt, then ass, then whichever's nearest, no order to it, the frame absorbing every shove so she never moves off her mark. Each one fucks until he's finished and then stays, buried to the root, leaning his weight down on the small of her back to catch his breath, using her like a place to rest before he pulls out and the next steps up into the gap. Nobody talks to her. She's the bench they're all taking a turn on.</p><p>And every load that goes in lands with nowhere to go. The socket at the base of her spine sits empty where the drain used to bolt in — the ass that used to TAKE something out of a man now just filling, over and over, the drain inverted. Each one that gets held in her feeds the plate under her sternum. The charge stacks behind it and climbs, and the read-out ticks and does not drop.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That's it — hold it in when you're done. Every one you leave sitting in there winds the number tighter than the fuck did.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Don't rush the line. It's not going anywhere. It's a fitting.</div></div><p>Furniture with a hole. Best kind.</p><p>Move — my turn. And don't pull out slow, leave it in there for the meter.</p><p>Still tight after all that? Machine earns its keep.</p>
<<set $game_state.current_canvas = "cell_use_frame">>
<<set $game_state.current_node = "cell_use_frame.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_frame.n3") === -1) { vn.push("cell_use_frame.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_frame_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_frame_held_inside_marks_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>One of them doesn't pull out at all. He drives in to the root and stops, grinds her down flat onto the bar and just holds there — a full beat, two, hips flush to her ass — while his hands go to work on everything else. A fistful of hair cranks her head back off the frame. The other hand wraps her throat from behind and squeezes until the read-out jumps, then lets go and leaves the print of every finger behind on her skin.</p><p>He slaps the ass he's still buried in, hard, twice, watches the handprint bloom red and stay. Digs his thumbs into her hips until they bruise, marking his grip where the next man can see it. By the time he finally pulls out she's covered — handprints across both cheeks, choke-marks coming up at her throat, drool dried on her chin, thumb-bruises on her hips. A machine handled rough and left marked all over, so anyone in the room can read exactly where it's been.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Choke it while you're seated in. The readout spikes when it can't breathe — fastest way to run the number up is to make the thing come while it's strangling.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Mark it up. I want to be able to see everywhere it's been, from across the room.</div></div><p>Look at that print come up. Take a picture.</p><p>Leave your grip on it — save the next guy a handhold.</p>
<<set $game_state.current_canvas = "cell_use_frame">>
<<set $game_state.current_node = "cell_use_frame.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_frame.n4") === -1) { vn.push("cell_use_frame.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_frame_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>When they're finally done and the frame's cranked back and she's left bent over it — dripping, marked, still bolted to spec — the only thing in the room still doing work is the plate under her sternum. The read-out glows in the dim, and the number on it is higher than it was. A lot higher. It climbed with every load that got held in her and it did not come back down, because there is nowhere for it to go.</p><p>Bastien crouches at the front of the frame to read it. Not her face — the number. He watches it settle, holds a light to it, writes it down.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Up again. Big jump off this run. It takes it in and it can't give any of it back — that's the whole trick of the thing.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Nowhere near topped out. Log it, reset the frame. We're not done running numbers off it.</div></div>
<<set $game_state.current_canvas = "cell_use_frame">>
<<set $game_state.current_node = "cell_use_frame.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_frame.readout") === -1) { vn.push("cell_use_frame.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -12.0, true, null);setup.showEffectNotification();<</script>>
[[Let them unstrap her.->Location_captive_room]]<video src="./videos/scenes/cell_turns_the_queue_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>By the time she's down on her knees in the middle of the concrete, the crew's already strung out along the wall in a loose line, waiting their turn like it's a thing they've done before. Bastien doesn't get in the line. He hauls the chair round so it faces the whole row of them, sits, and clicks a timer open in his palm. His cock's already out. His eyes are on the plate under her sternum, not on her.</p><p>'Thirty a man to start,' he says to the room. 'Move her down the line, don't crowd. I want to see how fast this thing climbs when you keep it fed.' The first man's already got a fist in her hair, waiting on the word. Bastien thumbs the timer. 'Go.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Thirty a man. Don't crowd her — I want it steady, not a pile-on. Not yet.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Look at it. Bought and paid for, and it takes a line better than a turnstile.</div></div><p>'Fuckin' finally. Been standing here with it out.'</p><p>'Save some, she's not going anywhere.'</p><p>'It's got more holes than the three of us need. Line moves.'</p>
<<set $game_state.current_canvas = "cell_use_turns">>
<<set $game_state.current_node = "cell_use_turns.base">>
<<script>>setup.markCanvasTriggered("cell_use_turns");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_turns.base") === -1) { vn.push("cell_use_turns.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_turns_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_turns_passed_down_the_line_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The first one fucks her mouth till he's done and pulls out to finish across her cheek, and before she's coughed it clear the next man's already stepping up and turning her by the jaw. Mouth, then cunt, then mouth again — whoever's up takes whichever hole he wants and the crew calls it out while they wait. One holds her hips and fucks her through it kneeling; the next just wants her face and uses it. Nobody waits for her to be ready. Ready was never on the list.</p><p>It stacks on her and in her as it goes. Cum on her cheek, roped off her chin, a load left in her cunt that the next man just shoves back up into her with his own. By the fourth turn she's slick and dripping and the line hasn't even thinned. Bastien clicks the timer over at every swap and reads the plate, not her face, calling the number out loud like a man calling races.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Next. Don't be gentle — it doesn't get you a better number.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it goes. Half a point a load. Keep it moving, it's finally earning its keep.</div></div><p>'Turn it round, I want the cunt.'</p><p>'Swallowed that one — good little hole.'</p><p>'Hurry up, I'm not done looking at what I'm getting.'</p>
<<set $game_state.current_canvas = "cell_use_turns">>
<<set $game_state.current_node = "cell_use_turns.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_turns.n2") === -1) { vn.push("cell_use_turns.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_turns_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_turns_t5.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Someone gets tired of the line and bends her over a knee, spreads her ass open with both thumbs, and works into the one hole that used to do the taking. He fucks it rough and finishes deep and doesn't pull out till he's soft. And that's the joke of her nobody in the room knows: the drain lived here once, at the base of her spine, and the ass was the thing that TOOK something out of a man. The socket's stripped now. Empty. So it only fills her — the drain run backwards, a load in the ass that goes nowhere but up.</p><p>Someone else reaches under and works her clit while the first man's still buried in her, mean and deliberate, forcing her over whether she wants it or not — and when she clenches and comes the plate jumps, a hard tick, faster than any load did. Bastien sees it. 'There,' he says. 'That's the one that moves it.' He writes the number down. The charge climbs with nowhere left to go, and the stripped socket at the base of her spine does nothing at all.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> In the ass — that's where the good reading is. Trust me on that one.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. Forced, it spikes. Make the machine come, don't just fuck it — climbs twice as fast.</div></div><p>'Rip that little hole open, watch it just take it.'</p>
<<set $game_state.current_canvas = "cell_use_turns">>
<<set $game_state.current_node = "cell_use_turns.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_turns.n3") === -1) { vn.push("cell_use_turns.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_turns_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_turns_used_and_pissed_on_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>By the time the line's thinned she's a mess on the floor, leaking out of both holes, cum drying tight on her tits and her face. One of them steps up like it's his turn again, but he doesn't get hard — he just plants his feet over her, takes his cock in his hand, and pisses on her instead. Down her chest, into her open mouth, across the plate under her sternum, hosing off what the others left like she's a thing you rinse and not a person.</p><p>The crew loses it. Someone tips her chin up by the hair so it runs across her face and she has to hold it. Nobody stops him. When he's done he shakes it off on her and steps back into what's left of the line, and she kneels there in the wet of it, dripping, used up, a thing to empty into and onto and nothing more.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Christ. Fine — it rinses off same as anything else. Doesn't move the number, though. Do that on your own time.</div></div><p>'Open up, machine — waste not.'</p><p>'Look at it just take that too.'</p><p>'Rinse and reuse, boys.'</p>
<<set $game_state.current_canvas = "cell_use_turns">>
<<set $game_state.current_node = "cell_use_turns.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_turns.n4") === -1) { vn.push("cell_use_turns.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_turns_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/scenes/cell_turns_the_read_out_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>When the last of them steps back the room goes quiet except for her breathing. Bastien crouches in front of her — the only time he gets close — and wipes the plate under her sternum clean with his thumb so he can read it. The number's higher than when they started. A lot higher. It went up with every load and it jumped with the ones they forced, and it hasn't shed a single point, because there's nothing left in her that lets it.</p><p>He looks at it a long moment. Not at her — through her, at the reading, the way you'd check a gauge creeping toward a line you drew yourself. He clicks the pen and writes it down. He caps it. He doesn't look at her face once.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Higher than the last count, and it didn't drop a point. Nothing drains out of you anymore, does it. Good.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Faster than I thought. We'll do this again before it's had time to sit.</div></div>
<<set $game_state.current_canvas = "cell_use_turns">>
<<set $game_state.current_node = "cell_use_turns.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_turns.readout") === -1) { vn.push("cell_use_turns.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -12.0, true, null);setup.showEffectNotification();<</script>>
[[Wait for the next one.->Location_captive_room]]<p>The bolt drags back and for once it isn't just him in the gap. He holds the door open with one hand and lets three of them file past his shoulder into the cell, then crosses to the chair against the wall and sits, one ankle up on his knee, the notebook already open on his thigh. The three spread out around her the way men spread out around something cornered — looking, deciding what she is before a single hand's on her.</p><p>That's where she stops being a person. Three sets of eyes, three sets of hands, and the one in the chair who only wants the number after.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Go on. She's bought and paid for — there's nothing in there you can wear out that I'm precious about.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Mind the plate on her chest, though. I want to read it when you're done with her.</div></div><p>'This the one? Christ.'</p><p>'They really do build 'em with a hole for everything.'</p>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.base">>
<<script>>setup.markCanvasTriggered("cell_use_first_three");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.base") === -1) { vn.push("cell_use_first_three.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_first_three_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_first_three_manhandled_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>They don't wait for him to finish talking. A fist knots in her hair and turns her by it; another catches her wrists and wrenches them back; a third checks her over rougher than Bastien ever bothers — grabs her jaw, thumbs her mouth open, slaps her tits just to watch them move. Spit lands on her cheek and nobody wipes it off. A hand closes on her throat and squeezes, not to choke, just to feel her take it and hold there.</p><p>Bruises come up under the grips — handprints going red on her hips, on her arms, a mark on her jaw where a thumb dug in. First time it's more than one pair of hands. They pass her between them like they're testing she'll hold together.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Marks are fine. Nothing that opens the casing — she stays sealed.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Filthy machine's been handled worse than you three. Watch — she won't even flinch.</div></div><p>'Hold still, machine.'</p><p>'She don't even blink — spit on her, watch.'</p><p>'Grab-marks come up lovely on this one.'</p>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.n2") === -1) { vn.push("cell_use_first_three.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_first_three_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_first_three_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>They put her on her knees and open her at both ends at once. One fists her hair and feeds his cock down her throat till she gags around it, holds her there while drool ropes off her chin onto her tits; the other's behind her with his cock buried to the root in her cunt, driving her back onto the first with every thrust so she can't pull off either. She's a length of holes stretched between two men and worked from both directions.</p><p>Every stroke into her charges the core. The read-out under her sternum ticks up with it — and it does not tick back down. Bastien watches from the chair, writes one line, watches again.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> She was built to be used at both ends, weren't you. Filthy fucking thing.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Charge is climbing on penetration alone — note that. Nobody's even made her come yet.</div></div><p>'Look at her take it both ends.'</p><p>'Push her — there, all the way down, that's a good hole.'</p>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.n3") === -1) { vn.push("cell_use_first_three.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_first_three_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_first_three_dp_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The third doesn't wait his turn. They stack her — one under her with his cock in her cunt, one still fucking her throat, and the third lines up behind and shoves into her ass. The stripped socket sits empty at the base of her spine, right above where he's splitting her open. The ass used to be the drain — the part that TOOK something out of a man. Now it only fills her, the whole thing running backwards, and every cock in her stacks the charge higher.</p><p>Then one reaches under and works her clit while she's plugged at all three, forces the orgasm up out of her whether she'd give it or not — and that's when the plate jumps. A come wrung out of her spikes it worse than any thrust. At this load it isn't pleasure landing anymore; it's charge with nowhere to go, the overflow gone hot and wrong under her sternum, and it climbs and climbs.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it is. Every hole loaded and the number won't stop.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Make her come — that's the one. Forced spikes it hardest. Confirmed.</div></div><p>'All three holes — she's fuckin' full.'</p><p>'Feel that? Something's buzzing inside her.'</p><p>'Machine's overheating — don't stop, wring it out of her.'</p>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.n4") === -1) { vn.push("cell_use_first_three.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_first_three_Node_n5">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_first_three_they_finish_on_in_her_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>They finish where they like. One pulls out of her throat and empties across her face — cum in her lashes, on her lips, dripping off her chin. One comes deep in her cunt and one across her back, ropes of it laid over the handprints they left there. They wipe their cocks clean on her tits and her hair when they're done with her.</p><p>She kneels there striped and dripping and does not move. Bastien counts the loads off the plate — every one of them registered, every one banked, none of it going anywhere.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Look at the state of you. Passed round and filled up like the thing you are.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Every load logged on the plate. She banks all of it — nothing drains.</div></div><p>'Open your mouth, machine — there, hold it.'</p><p>'All over her back, look at that.'</p><p>'Leave it on her. Let him see what he paid for.'</p>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.n5">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.n5") === -1) { vn.push("cell_use_first_three.n5"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_first_three_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>When they've filed out he crosses the cell, crouches in front of her, and wipes a smear of cum off the plate under her sternum with his thumb to read it clean. The read-out has climbed hard — further in one session than in a week of just him alone. He doesn't look at her face. He looks at the number, writes it down, and underlines it.</p><p>The strain's up with it. The charge stacks behind the plate with nowhere to go, hotter each load, and the meter only ever runs one direction.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Three at once and forced to come — fastest climb yet. Good.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Charge has nowhere to go and it's stacking. Note the strain.</div></div>
<<set $game_state.current_canvas = "cell_use_first_three">>
<<set $game_state.current_node = "cell_use_first_three.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_first_three.readout") === -1) { vn.push("cell_use_first_three.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -12.0, true, null);setup.showEffectNotification();<</script>>
[[Stay on the floor.->Location_captive_room]]<p>The notebook comes in before he does — a cheap spiral thing he sets on the crate, squares up with the edge, uncaps a pen. Then he drags the chair round to face her and sits, not close, the way a man settles in to watch a gauge rather than fuck a woman. 'Not fucking you today,' he says, almost bored. He taps his own sternum, where hers carries the plate. 'Today I want to see the needle move. You come, that thing counts it. So we're going to find out how many I can get out of you before it complains.'</p><p>He leans in and reads the read-out under her sternum through the strip of grime, and he writes that number down before he's laid a hand on her — a baseline, neat, underlined twice. Cold about it. Curious. 'Filthy little machine,' he says, unhurried, more to the page than to her. 'Let's see what you do.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Not fucking you today. Today I want to see the needle move.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> You come, that thing counts it. We're going to find out how many I can get out of you before it complains.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Filthy little machine. Let's see what you do.</div></div>
<<set $game_state.current_canvas = "cell_use_charge_test">>
<<set $game_state.current_node = "cell_use_charge_test.base">>
<<script>>setup.markCanvasTriggered("cell_use_charge_test");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_charge_test.base") === -1) { vn.push("cell_use_charge_test.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_charge_test_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_charge_test_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He works two fingers into her cunt without ceremony and starts — not for her, never for her, his eyes stay up on the plate the whole time. Slow, patient, the heel of his hand grinding a circle while his thumb finds the spot and stays on it. He isn't trying to please her. He's trying to trip a switch. 'There it is,' he murmurs, feeling her clench around him, and he doesn't sound pleased so much as confirmed. 'Wet already. Built to. You can strip everything out of a thing and it still slicks up on command like a good little hole.'</p><p>When the fingers stall he puts his mouth on her too — tongue flat and mean and efficient, spit and friction, throttling her up the way you'd rev an engine — and the whole time one eye stays on the read-out under her sternum, waiting for the twitch. 'That's it. Right up to it.' He pulls back a fraction and keeps her hanging there, deliberate. 'I want the number, not you. Don't matter what you want.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it is. Wet already. Built to. You can strip everything out of a thing and it still slicks up on command like a good little hole.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That's it. Right up to it.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> I want the number, not you. Don't matter what you want.</div></div>
<<set $game_state.current_canvas = "cell_use_charge_test">>
<<set $game_state.current_node = "cell_use_charge_test.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_charge_test.n2") === -1) { vn.push("cell_use_charge_test.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_charge_test_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_charge_test_she_comes_degraded_for_it_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>It goes off without her say-so — her cunt clamps down on his fingers and she comes, hard, hips jerking up into a hand that owns her, a sound tearing out of her that isn't a choice. He doesn't ease off through it; he holds her on the edge and wrings it out, watching her body betray her from the inside. 'There she is,' he breathes. 'Look at you. Filthy little machine, creaming all over my hand, gagging for it — didn't even ask you nice.'</p><p>Under her sternum the read-out jumps — not a crawl, a jump, the forced one spiking it the way the slow ones never do — and the charge it books has nowhere to go: the socket at the base of her spine sits empty where the drain used to seat, so it just stacks, hot, behind the plate. He watches the number leap and grins and writes it down. Not her face. The number. He wipes his fingers off on her thigh and reads the new digits out loud like a score.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There she is. Look at you. Filthy little machine, creaming all over my hand, gagging for it — didn't even ask you nice.</div></div>
<<set $game_state.current_canvas = "cell_use_charge_test">>
<<set $game_state.current_node = "cell_use_charge_test.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_charge_test.n3") === -1) { vn.push("cell_use_charge_test.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_charge_test_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_charge_test_again_overstim_slaps_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He goes straight back down on her before the first has finished ringing out, and this time it's too much and that's the point — fingers hard on a clit still fizzing, no mercy in it, dragging her up the raw wall of it while she's still shaking. When she flinches away he slaps her for it, open-handed, across the cunt — a wet crack — then again across the inside of her thigh, watching the red bloom up under his palm. 'Hold still. You'll move the number or you'll wear my hand, machine. Pick one.'</p><p>The second one hurts coming out — the overflow's got nowhere to sit and it turns the pleasure to a hot ache behind her ribs — but he forces it anyway, that's the whole trick, the forced ones move it fastest. She comes with a bruise already coming up on her thigh and a handprint on her cunt, jerking against a grip that doesn't care, and the read-out climbs again. He checks the plate before he checks anything else. 'Good. Again.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Hold still. You'll move the number or you'll wear my hand, machine. Pick one.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Good. Again.</div></div>
<<set $game_state.current_canvas = "cell_use_charge_test">>
<<set $game_state.current_node = "cell_use_charge_test.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_charge_test.n4") === -1) { vn.push("cell_use_charge_test.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_charge_test_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_charge_test_again_read_out_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He sits back on his heels, flexes his slick hand, and writes the new figure under the last — a little column building down the page now, each line higher than the one above it. He tips the plate toward the light with two fingers under her chin, not to see her, to read her, comparing the digits against the number he set down at the start. Then he turns the page. 'Climbed,' he says, satisfied, tapping the pen. 'You went up three just now. Nice steep little curve you've got.'</p><p>It doesn't drop back. That's the thing he's writing down and she isn't saying: the read-out ticks up and it stays up, every one he forces out of her stacked behind the sternum plate with the socket at her spine sitting empty and useless to bleed any of it off. He caps the pen, uncaps it again, gets comfortable on the crate. 'We've got all the time in the world,' he says, and puts his hand back between her legs. 'Again.'</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Climbed. You went up three just now. Nice steep little curve you've got.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> We've got all the time in the world. Again.</div></div>
<<set $game_state.current_canvas = "cell_use_charge_test">>
<<set $game_state.current_node = "cell_use_charge_test.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_charge_test.readout") === -1) { vn.push("cell_use_charge_test.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -10.0, true, null);setup.showEffectNotification();<</script>>
[[Let him finish writing.->Location_captive_room]]<video src="./videos/scenes/cell_inventory_the_order_t4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>The door lets him in and no one else. He has a folding chair under one arm; he snaps it open a body-length from her and drops into it like a man come to look over stock he already owns. No hurry in him. He sits with his knees apart and looks her up and down the way you'd look at a crate you paid too much for and mean to get your money's worth out of, and lets the quiet sit until it's clear the quiet is his too.</p><p>She stands where the room keeps her. He takes his time getting to the point, because taking his time is part of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Take it off. All of it. Let's see what I paid for.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Slow. I've got a whole drawer of notes on you upstairs and not one of them tells me what a thing like you is worth. So I'll price you myself.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.base">>
<<script>>setup.markCanvasTriggered("cell_use_inventory");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.base") === -1) { vn.push("cell_use_inventory.base"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_n2">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_strip_on_command_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Her hands don't go to the collar fast enough — a second's hesitation, a beat too long deciding whether to obey — and that's the whole of the excuse he wants. He comes up out of the chair and he's on her before the pause is done, gets a fist in the cloth at her shoulder and tears it down off her in one hard pull, seam giving with a rip, already grabbing the next piece before the first has hit the floor. He strips her the way you'd tear the wrap off stock you paid too much for and can't be bothered to unpick — rough, fast, cloth going in his hands — until she's bare and the torn scraps are around her feet.</p><p>He doesn't do it because he has to. He does it because making her do it herself is slower, and because tearing it off her tells her what she is here quicker than any order could — a thing to be unwrapped, not a thing to be asked. When she's bare he steps back to the chair, cock already out, and looks her over like he's checking he got what he paid for.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Too slow. When I say off, it comes off. I'll not ask a machine twice.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. Bought and paid for, and not even worth the wrapping. Let's see what's under it.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.n2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.n2") === -1) { vn.push("cell_use_inventory.n2"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_n3">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_the_inspection_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He gets up only when she's bare. He checks her over like stock — turns her by the hip, spreads her open with both thumbs, works two fingers up into her cunt and a third into her ass in the same breath, no interest at all in whether she's ready for any of it.</p><p>She doesn't answer him, so he spits in her face, unhurried, right across the cheek, and wipes his wet hand off on her tits. Then his thumb finds the empty socket at the base of her spine, and he stops, and works the tip of it into the hole, and laughs.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Nice and tight for a thing that's been passed round half the docks. They tell me you were a fixture in a whorehouse. Doesn't feel used up. Yet.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it is. That's where the party trick lived. Empty as a robbed till. Somebody's had it out of you — so now you're just holes and a bill.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.n3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.n3") === -1) { vn.push("cell_use_inventory.n3"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_n4">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_he_fucks_her_throat_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He doesn't ask her to suck it. He fists a handful of her hair, drags her down onto his cock, and fucks it straight down her throat — holds her there past the gag, past the point her eyes run and drool ropes off her chin onto her tits, then hauls her up for one wet breath and shoves her back down onto it.</p><p>He uses her face like it owes him money, both hands in her hair now, driving her throat down the length of him at his own pace and holding her there each time until her whole body kicks. She takes all of it, because taking it is the only thing left that she does.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There's a good hole.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That's it. Choke on it. That part of you still works, at least.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.n4">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.n4") === -1) { vn.push("cell_use_inventory.n4"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_n5">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He pulls her up off his cock, turns her, and folds her over the table with one hand flat between her shoulder blades, holding her down against nothing because she isn't fighting. Then he pushes into her cunt and fucks her the way a man saws a board he means to use — unhurried, all the way through, hand pressing her cheek to the wood.</p><p>When he spends himself in her he doesn't make a sound. The charge comes off him and goes into her and stops. It climbs up behind the plate under her sternum and stacks there against the wall of the stripped socket, turning over, looking for the way out, and not finding one.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That's one in the tank. Now let's see if any of it goes anywhere.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.n5">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.n5") === -1) { vn.push("cell_use_inventory.n5"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_n6">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_he_takes_her_ass_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He pulls out of her cunt still hard, works the head against her ass, and pushes in slow. It takes him longer here, and it's tighter, and he doesn't care. He holds her by the hips and drives up into her ass an inch from the empty socket at the base of her spine — the hole that used to be the drain, the thing that TOOK, that reached down a man's spine and pulled something loose.</p><p>Now it only fills her. Every stroke stacks the charge higher with nowhere for it to run, and when he empties himself into her ass she gets all of it and there is nothing on the far end of the take — no man gone soft and honest and hers — just his cum in her and the meter behind her sternum jumping.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> That used to be the good one, didn't it. The one that emptied a man out and left him yours. Look at it now — just another hole I get to use.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.n6">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.n6") === -1) { vn.push("cell_use_inventory.n6"); }<</script>>
<<nobr>>
<<link "Continue" "Canvas_cell_use_inventory_Node_readout">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><video src="./videos/sex/cell_inventory_he_finishes_the_read_out_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He stays buried in her ass a beat after he finishes, breathing, then pulls out and lets it run. He wipes his cock off on the curve of her ass and steps back to look at her — bent over his table, cheek on the wood, holding what he put in her.</p><p>The read-out on the plate under her sternum ticks up one, and does not tick back down. It never ticks back down now. He watches the number, not her; takes the notebook out of his coat, and writes it in the column beside the others.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There. Up, and holding. Whatever's meant to bleed that off isn't working — because I've got the part that does it sitting in a drawer upstairs. You just fill.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Good. We'll run it again once you've settled.</div></div>
<<set $game_state.current_canvas = "cell_use_inventory">>
<<set $game_state.current_node = "cell_use_inventory.readout">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_use_inventory.readout") === -1) { vn.push("cell_use_inventory.readout"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.applyAndNotifyTrait("player", null, "energy", "add", -10.0, true, null);setup.showEffectNotification();<</script>>
[[Get up off the table.->Location_captive_room]]<p>She comes up out of it slowly, which is new. There is a ceiling, painted a long time ago, and a light in a cage that does not turn off. A bed bolted to the floor. A chair that isn't. A table with nothing on it. In the middle of the floor there is a drain, because whoever built this room expected to hose it out.</p><p>She reaches back and finds the seam at the base of her spine open and empty. It doesn't hurt. That's the part she keeps checking — it doesn't hurt, the way a missing tooth doesn't hurt, and her hand goes back to it three times before she makes it stop.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Charge comes in. Charge has to go somewhere.</div></div><p>There is a door. It is steel and newer than the room, and it has no handle on this side. She sits on the edge of the bed with her hands in her lap and listens to men moving on the other side of it, and she works out, without any particular feeling about it, that she can lie down or she can pay attention, and that those are the two things there are.</p>
<<set $game_state.current_canvas = "captivity_intro">>
<<set $game_state.current_node = "captivity_intro.base">>
<<script>>setup.markCanvasTriggered("captivity_intro");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("captivity_intro.base") === -1) { vn.push("captivity_intro.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Sit up.->Location_captive_room]]<img src="./videos/scenes/captivity_taken.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>It's the newspaper she notices, half a street back — folded the same way, held by the same patient man who stepped aside to let them carry her past. He doesn't hurry. He doesn't need to. The van is already at the kerb behind her and the dock is long and there is nowhere on it to go that isn't further out.</p><p>This time nobody bothers with the bag until she's inside. A hand finds the seam at the base of her spine and works it, patient, the way a man works a jar lid, until it gives. The weight of the drain comes off her. Somebody says something about the boss wanting a proper look this time, and somebody else laughs, and then the bag.</p>
<<set $game_state.current_canvas = "captivity_recovery">>
<<set $game_state.current_node = "captivity_recovery.base">>
<<script>>setup.markCanvasTriggered("captivity_recovery");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("captivity_recovery.base") === -1) { vn.push("captivity_recovery.base"); }<</script>>
<<nobr>>
<<link "Go under." "Location_captive_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "captivity_entered", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She doesn't hear the door. One second she's got the ledger under her arm and the stair in front of her; the next there's a bag over her head, a knee in her back, and hands that know exactly how much pressure a body takes before it stops fighting. Nobody says a word — professionals. The last thing she registers before the dark is a man at the foot of the stair folding his newspaper, the same patient watcher from The House, stepping aside to let them carry her past.</p><p>She comes to upright in a chair that isn't restraining her, which is its own kind of message. A back room, warm light, a door that's shut and not locked. Across a plain table a man watches her with the patience of someone who owns the building and everything in it — unhurried, certain, a voice that will never have to be raised because the room is already his.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Wren. That's the file Mercer keeps you under, so it's the one I'll use. You've been busy. Doss. Rourke. My man Marsh, who woke up Monday poorer and stupider and can't tell me why. A great deal of digging, for one of Mercer's assets — into people who are mine.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Don't reach for whatever cover you had ready. I knew you were Mercer's before you finished your first drink down here; I hear things before they're said. That isn't what interests me.</div></div><p>He leans in, unhurried, and doesn't threaten her — doesn't reach for anything. He looks at her the way you'd look at the first genuinely interesting thing to cross your desk in years.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> What interests me is that when my man watched you leave The House, he swore the girl who walked out wasn't quite a girl. And my people are very rarely wrong about the things that unsettle them. So before I decide what you're worth — to me, and to the people I answer to, who'd pay a great deal for the honest answer — I'll ask you plainly. What are you, really?</div></div><p>The question hangs. She has covers for men who want her body and men who want her obedience, and none at all for a man who wants the one thing under the chip she can't reach herself. Somewhere below the amputation, something turns over at being seen. She doesn't answer. He reads the not-answering, nods as though it told him plenty, and lifts two fingers — and the bag comes down again.</p><p>Hands under her arms. A stair, and then further down than a stair should go. The salt smell of the docks thins out and stops. Somewhere in the dark someone finds the seam at the base of her spine, working it the way a man works a jar lid, patient, until it gives — and the weight of the drain comes off her and does not come back. She goes under before she can count the turns.</p>
<<set $game_state.current_canvas = "hunt_the_grab">>
<<set $game_state.current_node = "hunt_the_grab.base">>
<<script>>setup.markCanvasTriggered("hunt_the_grab");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_the_grab.base") === -1) { vn.push("hunt_the_grab.base"); }<</script>>
<<nobr>>
<<link "Go under." "Location_captive_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "bastien_revealed", "set");<</script>><<script>>setup.applyAndNotifyFlag("player", null, "captivity_entered", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><img src="./videos/scenes/crew_den_interior.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The key's under the third stair, like Marsh said. Inside, the rooms have the stillness of a place people left fast and never came back to — a card game abandoned mid-hand, a coat that fits no one now, a fridge that gave out weeks ago and was never opened since. Doss and Rourke lived here, and died somewhere else. She takes the place apart the way she takes everything apart: cold, thorough, patient.</p>
<<set $game_state.current_canvas = "hunt_crew_den_search">>
<<set $game_state.current_node = "hunt_crew_den_search.base">>
<<script>>setup.markCanvasTriggered("hunt_crew_den_search");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_crew_den_search.base") === -1) { vn.push("hunt_crew_den_search.base"); }<</script>>
<<nobr>>
<<link "Take the place apart." "Canvas_hunt_crew_den_search_Node_find">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Not yet." "Location_underworld_strip">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>It's in a strongbox behind the water heater — the one thing in the place worth locking. A run of ledgers, tallies of cargo that never touched a legal dock, and a name at the top of every page: the man the crew answered to, whose coin moved through Doss's hands and out into the company's blind spots. Not Cain. Someone she's never heard of. Bastien.</p><p>She has it now — a person at the end of the trail, a place to put the hunt. She pockets the ledger and turns for the stair.</p>
<<set $game_state.current_canvas = "hunt_crew_den_search">>
<<set $game_state.current_node = "hunt_crew_den_search.find">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_crew_den_search.find") === -1) { vn.push("hunt_crew_den_search.find"); }<</script>>
<<nobr>>
<<link "Bastien. Take the ledger and go." "Canvas_hunt_the_grab_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "bastien_found", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><img src="./videos/scenes/marsh_booking.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Marsh comes in on the hour, exact. A small neat man who checks the room's exits before he checks the girl, and takes the one the madam pointed him to without a word — her, tonight, in Rue's place. He doesn't notice the swap, or doesn't care; he wants the hour he paid for. Whatever a careful man keeps behind his face, she'll have to work him loose for it — and she knows the one act that opens him.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_false"}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})>>
<p>The drain sits cold against her hip — spent on Renner, never recharged. She can take Marsh's hour for the coin, but the truth stays locked behind his face until the weapon's live. Recharge it at the cradle and take his next Sunday.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_false"}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<p>She's carrying the emitter, not the drain — wrong weapon for this work. She can take Marsh's hour for the coin, but she'll get nothing out of him. Swap to the drain at her bench and take his next Sunday.</p>
<</if>>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.base">>
<<script>>setup.markCanvasTriggered("hunt_marsh_sunday");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.base") === -1) { vn.push("hunt_marsh_sunday.base"); }<</script>>
<<nobr>>
<<link "Take him — earn the hour." "Canvas_hunt_marsh_sunday_Node_oral">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Not tonight." "Location_underworld_strip">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_oral_t5"] to ($game_state.media_cycle["sex_marsh_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_oral_t5/c7caf640af8.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_oral_t5/c41b3d37d25.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_oral_t5/c83d39c7bfe.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_oral_t5/ccd4b0e594c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees, she takes him in her mouth and works him the way the trade taught her, but slower, watching — reading where the careful in him loosens and where it doesn't. He rests a hand in her hair, polite even here, and lets his head go back. Getting there.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.oral">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.oral") === -1) { vn.push("hunt_marsh_sunday.oral"); }<</script>>
<<nobr>>
<<link "Keep working him." "Canvas_hunt_marsh_sunday_Node_oral">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Climb on and ride him." "Canvas_hunt_marsh_sunday_Node_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn around — give him your ass." "Canvas_hunt_marsh_sunday_Node_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_hunt_marsh_sunday_Node_finish_soft">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_ride_t5"] to ($game_state.media_cycle["sex_marsh_ride_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_ride_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_ride_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_ride_t5/c4f9cda0ca4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_ride_t5/c79f7199221.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_ride_t5/c996d676d6d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_ride_t5/cb9bc227b81.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She climbs on and rides him slow, keeping the pace hers, watching the careful man forget to be careful. He grips her hips and lets go of something — not much, but she's not after much. She's after the one thing men give up when she takes them the last way.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.vaginal">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.vaginal") === -1) { vn.push("hunt_marsh_sunday.vaginal"); }<</script>>
<<nobr>>
<<link "Keep riding him." "Canvas_hunt_marsh_sunday_Node_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_hunt_marsh_sunday_Node_oral">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn around — give him your ass." "Canvas_hunt_marsh_sunday_Node_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_hunt_marsh_sunday_Node_finish_soft">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_anal_t5"] to ($game_state.media_cycle["sex_marsh_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_anal_t5/c04e940ad74.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_anal_t5/c64b03ce705.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_anal_t5/c942c12efcf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_anal_t5/cb0ac824f33.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She turns and gives him her ass, and he takes it the way careful men take the thing they'd never ask for out loud — slow, greedy, undone by it. This is the act that opens him. Bent over the bed with his hands clamped on her hips, she waits for the moment his whole guarded weight goes soft, and reaches, if she's carrying it, for the drain.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.anal">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.anal") === -1) { vn.push("hunt_marsh_sunday.anal"); }<</script>>
<<nobr>>
<<link "Take it — work him to the edge." "Canvas_hunt_marsh_sunday_Node_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back onto his cock." "Canvas_hunt_marsh_sunday_Node_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_false"}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — take the drain." "Canvas_hunt_marsh_sunday_Node_drain">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc3') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass." "Canvas_hunt_marsh_sunday_Node_finish_ass_paid">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_false"}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc4') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — the drain's spent." "Canvas_hunt_marsh_sunday_Node_finish_ass_nodrain">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}, {"type": "flag", "subject": "player", "flag_key": "crew_known", "operator": "is_false"}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<span @class="setup.isChoiceVisited('hunt_marsh_sunday:cc5') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass — you're carrying the wrong gear." "Canvas_hunt_marsh_sunday_Node_finish_ass_nodrain">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hunt_marsh_sunday:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><video src="./videos/sex/marsh_finish_ass_drain_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>He finishes in her ass, and the drain wakes with him — the same act that hands her nothing with a paying man hands her everything with this one. The careful goes out of him all at once, loose and honest, his guarded weight gone soft against her back. She asks the only thing she came for: where the crew lived — Doss, Rourke, the rest. He tells her. A dead man's rooms down past the market, nobody's cleared them, the door still answers to a key under the third stair. In a few minutes he'll surface remembering a good hour and nothing else.</p><p>On her way out a man at the bar folds his paper and watches her go — not the madam's muscle, someone else's, patient about it. She clocks him clocking her and files it. Careful men keep careful company; someone just paid attention to the wrong girl asking the wrong questions.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.drain">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.drain") === -1) { vn.push("hunt_marsh_sunday.drain"); }<</script>>
<<nobr>>
<<link "Take the address and go." "Location_underworld_strip">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "crew_known", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_finish_facial_t5"] to ($game_state.media_cycle["sex_marsh_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_finish_facial_t5/c4fafc11b46.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_finish_facial_t5/c4ff543aef2.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_finish_facial_t5/c869acbdd6b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_finish_facial_t5/cb727dd478d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_finish_inside_t5"] to ($game_state.media_cycle["sex_marsh_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_finish_inside_t5/c12d725a7ca.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_finish_inside_t5/c52f3226be0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_finish_inside_t5/ca7f453e47e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_finish_inside_t5/ce583676f24.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>><p>He finishes the way he came in — quiet, contained, already reaching for his coat before his breath's back. A careful man keeps his softness behind him when he doesn't lose the last of himself, and the mouth and the cunt don't cost him that. She takes the coin he leaves and lets him go. Whatever he knows is still behind his face. It's the ass that opens him — she'll need him again, and the drain worn and charged.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.finish_soft">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.finish_soft") === -1) { vn.push("hunt_marsh_sunday.finish_soft"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Clean up. Collect.->Location_underworld_brothel]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_finish_ass_paid_t5"] to ($game_state.media_cycle["sex_marsh_finish_ass_paid_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_finish_ass_paid_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_finish_ass_paid_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_finish_ass_paid_t5/c1822635c2a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_finish_ass_paid_t5/c3681576600.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_finish_ass_paid_t5/cc44fa20b5f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_finish_ass_paid_t5/cd52b8a5539.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes in her ass and goes slack against her, spent and honest and empty of anything she still needs — she took what was behind his face already, and a man only has the one secret worth the drain. Now he's just a careful regular with a standing Sunday, tipping fair, none the wiser. She straightens, takes the coin, and lets him go.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.finish_ass_paid">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.finish_ass_paid") === -1) { vn.push("hunt_marsh_sunday.finish_ass_paid"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Clean up. Collect.->Location_underworld_brothel]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_marsh_finish_ass_nodrain_t5"] to ($game_state.media_cycle["sex_marsh_finish_ass_nodrain_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_marsh_finish_ass_nodrain_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_marsh_finish_ass_nodrain_t5"]>><<if _mc is 0>><video src="./videos/sex/marsh_finish_ass_nodrain_t5/c1c8eee83cb.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/marsh_finish_ass_nodrain_t5/c17da28b611.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/marsh_finish_ass_nodrain_t5/cd93aba33bf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/marsh_finish_ass_nodrain_t5/ce8bc87ef43.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He finishes in her ass and the careful goes out of him — loose, open, everything he knows a touch away — and she's got nothing on her to take it with. Without the drain worn and charged he's just a spent john with a soft face and a secret still locked behind it. She takes his coin and files the miss. Recharge the drain at the cradle, carry it, take his Sunday again — the truth's a touch away once the weapon's on her.</p>
<<set $game_state.current_canvas = "hunt_marsh_sunday">>
<<set $game_state.current_node = "hunt_marsh_sunday.finish_ass_nodrain">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_sunday.finish_ass_nodrain") === -1) { vn.push("hunt_marsh_sunday.finish_ass_nodrain"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Clean up. Collect.->Location_underworld_brothel]]<p>Sunday, and the House keeps its one clock. Rue's at her chair with the ledger open to tonight's page, the far room already blocked out in her own hand. A few hours yet before the careful little man walks in expecting her.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Your Sunday standing. The careful little man, same hour every week. I want tonight.</div></div><p>A slow look up from the ledger, reassessing. She knows exactly who the girl means, and she doesn't ask why.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> Marsh. Quiet money, tips fair, never any trouble — the kind of regular you don't hand off. But I could use a night that's mine, and you've got the look of someone who wants him for more than the coin.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> So take tonight. Tonight, mind — not my Sundays. He's on the hour, the far room. Whatever your business is, keep it off my floor, and he never hears from me that the girl got swapped. You want him again, you come ask me again.</div></div>
<<set $game_state.current_canvas = "hunt_marsh_scheme">>
<<set $game_state.current_node = "hunt_marsh_scheme.base">>
<<script>>setup.markCanvasTriggered("hunt_marsh_scheme");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hunt_marsh_scheme.base") === -1) { vn.push("hunt_marsh_scheme.base"); }<</script>>
<<nobr>>
<<link "Tonight, then. The far room." "Location_underworld_brothel">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "rue_met", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Not this week." "Location_underworld_brothel">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The floor's quiet this hour — a girl asleep in a chair, a lamp left burning low, the rooms all shut. The madam runs the House by day; nothing moves through here until she's back at her post. Come back when she's in, if it's work you're after.</p>
<<set $game_state.current_canvas = "brothel_offhours">>
<<set $game_state.current_node = "brothel_offhours.base">>
<<script>>setup.markCanvasTriggered("brothel_offhours");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("brothel_offhours.base") === -1) { vn.push("brothel_offhours.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Head back up.->Location_underworld_strip]]<p>Rue keeps her chair by the rooms, ledger open, watching the floor the way a dealer watches a table. She nods when she sees her — not warmth, just the level look of one working woman clocking another.</p>
<<set $game_state.current_canvas = "rue_hub">>
<<set $game_state.current_node = "rue_hub.base">>
<<script>>setup.markCanvasTriggered("rue_hub");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rue_hub.base") === -1) { vn.push("rue_hub.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "brothel_hired", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('rue_hub:cc0') ? '' : 'unlocked-choice'">
<<link "Ask her for work." "Canvas_rue_hub_Node_work">><<script>>setup.markChoiceVisited("rue_hub:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "brothel_hired", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('rue_hub:cc1') ? '' : 'unlocked-choice'">
<<link "Trade a word." "Canvas_rue_hub_Node_talk">><<script>>setup.markChoiceVisited("rue_hub:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave her to it." "Location_underworld_brothel">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> I want the work. The rooms.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> Course you do. They all do, once they've been down here long enough to stop pretending otherwise.</div></div><p>She writes something in the ledger without looking up — a name, or a mark against one — and tips her head at the rooms. That's the whole hiring. No contract, no questions.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> Rooms are yours when you want them. Coin's yours after. Don't make me regret the chair I didn't get up from.</div></div>
<<set $game_state.current_canvas = "rue_hub">>
<<set $game_state.current_node = "rue_hub.work">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rue_hub.work") === -1) { vn.push("rue_hub.work"); }<</script>>
<<nobr>>
<<link "You won't." "Canvas_rue_hub_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "brothel_hired", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She trades what she hears, the small weather of the underworld — which men are talking, whose protection got thinner this week, who's asking after who. "You didn't get it off me," she says every time, and tells her anyway. It isn't much. But it's a face down here that isn't a mark and isn't an owner, and there aren't many of those.</p>
<<set $game_state.current_canvas = "rue_hub">>
<<set $game_state.current_node = "rue_hub.talk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rue_hub.talk") === -1) { vn.push("rue_hub.talk"); }<</script>>
<<nobr>>
<<link "Good talking." "Location_underworld_brothel">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><img src="./videos/scenes/rue_madam.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The woman by the rooms takes her measure before she's three steps in — the flat, unhurried read of someone who's priced a thousand bodies and is rarely wrong by much. She doesn't get up.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> Rue. I run this house. Not the muscle, not the owner — the floor. Which means I decide who works my rooms and who serves the few names worth keeping. You've got the look of someone who's been used for it before and stopped feeling it. Down here that's not a mark against you. That's the job.</div></div><p>She tips her head at the rooms behind her — the offer plain, no contract, no company, nobody signing the girl out at the end of it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/rue.jpg" alt="Rue" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Rue:</strong> Coin's yours when you're done, and nobody up top hears a word of it from me. You want in, you ask me. Simple as that.</div></div>
<<set $game_state.current_canvas = "rue_intro">>
<<set $game_state.current_node = "rue_intro.base">>
<<script>>setup.markCanvasTriggered("rue_intro");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rue_intro.base") === -1) { vn.push("rue_intro.base"); }<</script>>
<<nobr>>
<<link "Take her measure back." "Location_underworld_brothel">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "rue_introduced", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She slides onto the stool beside him and it's wrong before she's said a word. Out of the coveralls, in her own clean clothes, she's not the dock-rat who hauls his crates — she's a stranger sitting too close, and his bleary eyes narrow on her with the old wariness coming up through the drink.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...do I know you? You don't look like you tonight.</div></div><p>Even three drinks down he knows the shape of her is off. Nothing she wants from him happens while she's wearing the wrong face. The cover's back in her quarters, where it does her no good.</p>
<<set $game_state.current_canvas = "react_renner_nocover_anchor">>
<<set $game_state.current_node = "react_renner_nocover_anchor.base">>
<<script>>setup.markCanvasTriggered("react_renner_nocover_anchor");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("react_renner_nocover_anchor.base") === -1) { vn.push("react_renner_nocover_anchor.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave before he sobers on it.->Location_the_anchor]]<p>She comes onto the floor and Renner's head comes up wrong. The coveralls aren't on her — she's in her own clothes, the clean expensive line of them all out of place against the racks — and for a second he doesn't place her at all. Then the wariness: not recognition. Suspicion.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...the hell are you dressed like that for. Do I know you?</div></div><p>Wrong skin for this yard. Whatever she'd get done here today, she won't get it done looking like this. The cover's back in her quarters; it does her no good on the rack.</p>
<<set $game_state.current_canvas = "react_renner_nocover_depot">>
<<set $game_state.current_node = "react_renner_nocover_depot.base">>
<<script>>setup.markCanvasTriggered("react_renner_nocover_depot");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("react_renner_nocover_depot.base") === -1) { vn.push("react_renner_nocover_depot.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Get out before he looks harder.->Location_renner_depot]]<img src="./videos/scenes/renner_anchor.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Renner's at the end of the bar. She takes the stool and starts in — heard he's rebuilding, looking for work — and watches it land wrong. She's too clean. Too put-together for a woman who'd want a dock-rat's wages. He looks at her the way a man with enemies looks at a stranger who knows his business.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Whatever you are — cop, fed, one of theirs — I've got nothing to say to it. Off my stool.</div></div><p>He turns his shoulder and that's the end of it. Dressed like this she's not cheap hands; she's a question, and a man this far down doesn't answer questions. She'd have to come at him as something that doesn't get a second look — the cover's in her quarters, and it does her no good on the rack.</p>
<<set $game_state.current_canvas = "react_renner_threat">>
<<set $game_state.current_node = "react_renner_threat.base">>
<<script>>setup.markCanvasTriggered("react_renner_threat");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("react_renner_threat.base") === -1) { vn.push("react_renner_threat.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave it.->Location_the_anchor]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_anal_once", "operator": "is_true"}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_cheerup_alley_t5"] to ($game_state.media_cycle["sex_renner_cheerup_alley_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_cheerup_alley_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_cheerup_alley_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_cheerup_alley_t5/c2f12625060.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_cheerup_alley_t5/c007f3a0a9e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_cheerup_alley_t5/c8a9b68c694.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_cheerup_alley_t5/ced2d137d68.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He's wrecked tonight, and she does the thing that works. She gets him out of the bar and into the dark behind it and gives him whatever he needs — on her knees on the filthy concrete, or bent over a crate, the cold boss falling apart and the help putting him back together with her mouth and her ass. It's the one real thing she has, and she spends it on him like it's nothing, because it is.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_oral_once", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "renner_anal_once", "operator": "is_false"}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_cheerup_oral_t5"] to ($game_state.media_cycle["sex_renner_cheerup_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_cheerup_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_cheerup_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_cheerup_oral_t5/c3f7a9fddc5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_cheerup_oral_t5/c5e34c5d737.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_cheerup_oral_t5/c8c8352e5f1.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_cheerup_oral_t5/c0521ec1329.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>He's low tonight. She slips off the stool and into the dark beside the cigarette machine and takes his cock in her mouth right there, fast and devoted, the bartender pointedly not looking. He comes with his hand in her hair saying her work-name like a man saying his prayers wrong.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_oral_once", "operator": "is_false"}]})>>
<p>He's low tonight — too low to do anything but sit in it. She sits closer than she needs to, lets her thigh press his, lets her hand rest high on his leg where the wanting has somewhere to go. It's not much. But it makes the worst of the night about her instead of about the wreck of his life, and that's the seed of everything that comes after.</p>
<</if>>
<<set $game_state.current_canvas = "amb_renner_cheerup">>
<<set $game_state.current_node = "amb_renner_cheerup.base">>
<<script>>setup.markCanvasTriggered("amb_renner_cheerup");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("amb_renner_cheerup.base") === -1) { vn.push("amb_renner_cheerup.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave him steadier.->Location_the_anchor]]<img src="./videos/scenes/amb_renner_anchor_ruin.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 20}]})>>
<p>Two stools down he's telling the bartender a story about the depot before Cain — when it was something, when he was something. The bartender's heard it. So has she. Renner doesn't notice either of them stopped listening a while ago.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 20}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 40}]})>>
<p>There's a letter on the bar in front of him tonight he keeps not reading — blacklist stationery, the third this month. He drinks around it. When he catches her looking he turns it face-down, too late, and orders another.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 40}]})>>
<p>He's further gone tonight than she's seen him — talking too much, too loud, the rot all the way to the surface. What they made him part of. What burned. What he signed for and told himself he didn't understand. He won't remember saying any of it tomorrow. She'll remember all of it. It's the guilt the seduction never has to touch.</p>
<</if>>
<<set $game_state.current_canvas = "amb_renner_anchor_ruin">>
<<set $game_state.current_node = "amb_renner_anchor_ruin.base">>
<<script>>setup.markCanvasTriggered("amb_renner_anchor_ruin");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("amb_renner_anchor_ruin.base") === -1) { vn.push("amb_renner_anchor_ruin.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Let him drink.->Location_the_anchor]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 20}]})>>
<p>She's halfway up a ladder with a crate when she feels it — Renner, stopped at the bottom, not pretending to check a manifest, just watching her work. He looks away when she looks down. But he was watching, and now they both know he was.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 20}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 40}]})>>
<p>He finds reasons to be where she is — stands too close at the racks, hands her tools she didn't ask for, lets the backs of his fingers find her arm. "You're good at this," he says, like it costs him, and doesn't move away when he's said it.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 40}]})>>
<p>He doesn't bother with the pretext anymore. He takes the crate out of her hands, sets it down, and backs her into the shelving with his whole ruined weight against her. "Leave it," he says against her neck. "The invoices can wait. Come in the back."</p>
<</if>>
<<set $game_state.current_canvas = "walkin_renner_depot">>
<<set $game_state.current_node = "walkin_renner_depot.base">>
<<script>>setup.markCanvasTriggered("walkin_renner_depot");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("walkin_renner_depot.base") === -1) { vn.push("walkin_renner_depot.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[...->Location_renner_depot]]<p>He'll answer anything now, and remember none of it. She crouches to his eye level and asks, quiet, the way you'd read a meter.</p><span id="cascade-b1-beat-0"><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> The gear you sold Vance. What did it build?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Containment. Restraint rigs, suppression fields, a wet lab built to hold things that aren't supposed to be holdable. I didn't ask what. They paid on time and I had a business to save.</div></div><span id="cascade-b1-beat-1"><<linkreplace "What did Cain take?">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> When Cain hit the facility. What did he take?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Nothing. He freed it. Whatever they were holding in there, he let it out before he burned the place. The manifests after didn't add up — they were short bodies that weren't bodies. He wasn't robbing them. He was emptying a cage.</div></div><p>She files it somewhere the cradle can't reach. The monster she's sent to kill broke into the worst place these people built and let the prisoners walk out. It doesn't fit the briefing. Nothing about him fits the briefing, and the not-fitting is starting to have a shape.</p><span id="cascade-b1-beat-2"><<linkreplace "Why did Cain burn you down?">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Cain didn't just walk away from you. He came for you. Why?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Two of my people. Doss and Rourke. They moved my materials — and they were moving them to him too, feeding Cain off my own floor. When I found out I didn't just cut them loose. I did them, and everyone in their houses, the same night. He burned me to the slab for it. We've been at each other since.</div></div><p>She keeps her face a meter-reader's blank. So the burned yard she's been picking through wasn't collateral in someone else's war — it was the answer, and the two files she pulled out there are the men he's confessing to now. The wreck of him has a shape after all, and the shape is a debt.</p><span id="cascade-b1-beat-3"><<linkreplace "Where is he now?">><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Where would he go to ground?</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> I don't know. But those two came up out of the underworld before they ever came to me — that's where their kind runs. If Cain's anywhere, he's down there. That's all I... that's all I have.</div></div><span id="cascade-b1-beat-4"><<linkreplace "Tell him to forget.">><p>She tells him to forget, and he will. In a few minutes he'll surface blinking, his cock going soft and no memory of the last ten, certain only that the new hire is very good and he should keep her close. She fixes her clothes while the man who just handed her everything stares at nothing.</p>[[Leave him to come back to himself.->Location_renner_depot]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "renner_control_canvas">>
<<set $game_state.current_node = "renner_control_canvas.intro">>
<<script>>setup.markCanvasTriggered("renner_control_canvas");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("renner_control_canvas.intro") === -1) { vn.push("renner_control_canvas.intro"); }<</script>>
<<script>>advanceTime(3);<</script>><p>He goes soft and open the second she's done, the controls warm in her hand again — hers for a few minutes, and he won't remember a second of it. She already has what he knew. There's nothing left in him she needs; there's only what she feels like taking.</p>
<<set $game_state.current_canvas = "renner_control_canvas">>
<<set $game_state.current_node = "renner_control_canvas.again">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("renner_control_canvas.again") === -1) { vn.push("renner_control_canvas.again"); }<</script>>
<<nobr>>
<<link "Ask him again." "Canvas_renner_control_canvas_Node_again_ask">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Just leave him." "Location_renner_depot">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She crouches to his eye level and asks anyway — the gear, the two he buried, where the man went.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Two of mine — Doss and Rourke — feeding him off my floor. I did them and theirs, and he burned me for it. Where he went — down. It's all down in the underworld. That's everything I had. I swear it.</div></div><p>The same confession, in the same order — but this time she keeps the two names. He doesn't know he's said it before, and he never will. Whatever she already had, watching the wrecked man empty himself out for her on command is its own kind of confirmation. She fixes her clothes and leaves him to surface alone.</p>
<<set $game_state.current_canvas = "renner_control_canvas">>
<<set $game_state.current_node = "renner_control_canvas.again_ask">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("renner_control_canvas.again_ask") === -1) { vn.push("renner_control_canvas.again_ask"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Leave him to come back to himself.->Location_renner_depot]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<p>He finishes on her face with a groan that's half a sob, one hand cradling the back of her head like she's something he can't believe he's allowed. He'd give her anything right now. He doesn't know yet how much that's going to cost him.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_finish_facial_t5"] to ($game_state.media_cycle["sex_renner_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_finish_facial_t5/ca6028f32a7.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_finish_facial_t5/cc2ef54d54d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_finish_facial_t5/ce6f50a12d0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_finish_facial_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<p>He finishes inside her, holding her down on him, shaking. For a moment after he just breathes into her shoulder, wrecked and grateful, and she lets him have the moment because letting him have it is how she keeps him.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_finish_inside_t5"] to ($game_state.media_cycle["sex_renner_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_finish_inside_t5/c78dd1b8fc6.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_finish_inside_t5/cdfbe162355.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_finish_inside_t5/ce840505218.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_finish_inside_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<p>He finishes in her ass, and the controls slide warm into her hand — the give, the opening, his whole ruined weight gone pliant against her back. The same act that hands her nothing with Mercer hands her everything with him.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_finish_anal_t5"] to ($game_state.media_cycle["sex_renner_finish_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_finish_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_finish_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_finish_anal_t5/c293e348327.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_finish_anal_t5/ca75bdf1c40.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_finish_anal_t5/cc418bf2714.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_finish_anal_t5/cff8c06501b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>>
<<set $game_state.current_canvas = "loop_renner_finisher">>
<<set $game_state.current_node = "loop_renner_finisher.climax">>
<<script>>setup.markCanvasTriggered("loop_renner_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_renner_finisher.climax") === -1) { vn.push("loop_renner_finisher.climax"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_renner_finisher:cc0') ? '' : 'unlocked-choice'">
<<link "Straighten up." "Location_renner_depot">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_finisher:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_renner_finisher:cc1') ? '' : 'unlocked-choice'">
<<link "Take the keys." "Canvas_renner_control_canvas_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drains_done", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "names_known", "set", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "renner_drained", "set");<</script>><<script>>setup.applyAndNotifyFlag("player", null, "renner_leads_extracted", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_finisher:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_renner_finisher:cc2') ? '' : 'unlocked-choice'">
<<link "Take the keys." "Canvas_renner_control_canvas_Node_again">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drains_done", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "names_known", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_finisher:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_renner_finisher:cc3') ? '' : 'unlocked-choice'">
<<link "Let it go — the drain's empty. Recharge it at the cradle." "Location_renner_depot">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_finisher:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<span @class="setup.isChoiceVisited('loop_renner_finisher:cc4') ? '' : 'unlocked-choice'">
<<link "Let it go — wrong gear." "Location_renner_depot">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_finisher:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})))>>
<<script>>
try {
window._engineNoExitsDiag_loop_renner_finisher = [{"text": "Straighten up.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "lt", "value": 2}]}}, {"text": "Take the keys.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "lt", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]}}, {"text": "Take the keys.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "drains_done", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "gte", "value": 1}]}}, {"text": "Let it go \u2014 the drain's empty. Recharge it at the cradle.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "drain_charge", "operator": "lt", "value": 1}]}}, {"text": "Let it go \u2014 wrong gear.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "loop_renner_finisher",
choices: window._engineNoExitsDiag_loop_renner_finisher,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_loop_renner_finisher>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas loop_renner_finisher</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Navigation]]
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_loop_t5"] to ($game_state.media_cycle["sex_renner_loop_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_loop_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_loop_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_loop_t5/c88bd692ed4.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_loop_t5/cbf1782f954.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_loop_t5/cdd4c416ab0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_loop_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She locks the office door. He's already reaching for her — the cold boss gone, just a wrecked man who's spent all day waiting for the help to come and take him apart.</p>
<<set $game_state.current_canvas = "loop_renner_office_sex">>
<<set $game_state.current_node = "loop_renner_office_sex.intro">>
<<script>>setup.markCanvasTriggered("loop_renner_office_sex");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_renner_office_sex.intro") === -1) { vn.push("loop_renner_office_sex.intro"); }<</script>>
<<nobr>>
<<link "Get on your knees." "Canvas_loop_renner_office_sex_Node_base_oral_r">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_loop_oral_t5"] to ($game_state.media_cycle["sex_renner_loop_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_loop_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_loop_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_loop_oral_t5/c1e6b2a4bed.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_loop_oral_t5/c377a6771c3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_loop_oral_t5/c4078dc881e.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_loop_oral_t5/ce724bed764.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees between the chair and the desk, his cock in her mouth, his hand gentler in her hair than Mercer's hand has ever been. Renner holds on like he's afraid she'll stop.</p>
<<set $game_state.current_canvas = "loop_renner_office_sex">>
<<set $game_state.current_node = "loop_renner_office_sex.base_oral_r">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_renner_office_sex.base_oral_r") === -1) { vn.push("loop_renner_office_sex.base_oral_r"); }<</script>>
<<nobr>>
<<link "Keep sucking his cock." "Canvas_loop_renner_office_sex_Node_base_oral_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Climb on his cock." "Canvas_loop_renner_office_sex_Node_base_vaginal_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_renner_office_sex:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_loop_renner_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_office_sex:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_loop_vaginal_t5"] to ($game_state.media_cycle["sex_renner_loop_vaginal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_loop_vaginal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_loop_vaginal_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_loop_vaginal_t5/c7c8a68085f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_loop_vaginal_t5/c7f9cece542.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_loop_vaginal_t5/c9d59417c72.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_loop_vaginal_t5/ccfd2393d12.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She rides him in the office chair, slow, watching the last of his composure go. He says things he'll be ashamed of — please, and the false name she works under, and don't stop, don't ever stop.</p>
<<set $game_state.current_canvas = "loop_renner_office_sex">>
<<set $game_state.current_node = "loop_renner_office_sex.base_vaginal_r">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_renner_office_sex.base_vaginal_r") === -1) { vn.push("loop_renner_office_sex.base_vaginal_r"); }<</script>>
<<nobr>>
<<link "Take him." "Canvas_loop_renner_office_sex_Node_base_vaginal_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on his cock." "Canvas_loop_renner_office_sex_Node_base_oral_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 50}, {"type": "clothing_item", "item_id": "cover_dockhand", "operator": "equipped"}]})>>
<span @class="setup.isChoiceVisited('loop_renner_office_sex:cc1') ? '' : 'unlocked-choice'">
<<link "Turn around — give him your ass." "Canvas_loop_renner_office_sex_Node_base_doggy_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "renner_anal_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_office_sex:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Not yet — he has to be all the way broken first.">Not yet — he has to be all the way broken first.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('loop_renner_office_sex:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_loop_renner_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_office_sex:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_renner_loop_doggy_t5"] to ($game_state.media_cycle["sex_renner_loop_doggy_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_renner_loop_doggy_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_renner_loop_doggy_t5"]>><<if _mc is 0>><video src="./videos/sex/renner_loop_doggy_t5/c2d860ae082.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/renner_loop_doggy_t5/c22c386f83a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/renner_loop_doggy_t5/c157da6227a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/renner_loop_doggy_t5/cdc391571f0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent over his own desk, his cock in her ass, his hands shaking on her hips. This is the act that hands her the keys — and now that she's turned it on him once, she can take them again whenever she likes.</p>
<<set $game_state.current_canvas = "loop_renner_office_sex">>
<<set $game_state.current_node = "loop_renner_office_sex.base_doggy_r">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("loop_renner_office_sex.base_doggy_r") === -1) { vn.push("loop_renner_office_sex.base_doggy_r"); }<</script>>
<<nobr>>
<<link "Take it in the ass." "Canvas_loop_renner_office_sex_Node_base_doggy_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back on his cock." "Canvas_loop_renner_office_sex_Node_base_vaginal_r">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('loop_renner_office_sex:cc3') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass." "Canvas_loop_renner_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("loop_renner_office_sex:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><video src="./videos/sex/ma_renner_blowjob_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>Next afternoon, the office, like nothing happened — except she won't let it be like nothing happened. She waits until he's mid-sentence about a shipment, then asks him level, watching his face:</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> Did you like it. Yesterday. Or not.</div></div><p>He can't get out of it. She watches him try — the bluster, the deflection, the reach for the cold he used to have — and watches all of it fail, because the answer's already on him, plain. "You know I did," he says at last, like it's dragged out of him. And that's the thing she came for: not the yes, the surrender in having to say it. He's further in than he was, and he can feel it too.</p>
<<set $game_state.current_canvas = "ma_renner_blowjob">>
<<set $game_state.current_node = "ma_renner_blowjob.base">>
<<script>>setup.markCanvasTriggered("ma_renner_blowjob");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("ma_renner_blowjob.base") === -1) { vn.push("ma_renner_blowjob.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_renner", "corruption", "add", 3.0, false, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "ma_blowjob_seen", "set");<</script>>
[[Leave it sitting on him.->Location_renner_depot]]<video src="./videos/sex/renner_blowjob_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>His wanting runs ahead of him all day now. She picks the afternoon — the door shut, the yard empty, his hands shaking around a pen that isn't writing anything. She crosses to him and goes down on her knees in front of the chair, and for once he doesn't pretend he doesn't know what's happening.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...you don't have to.</div></div><p>It's the first kind thing he's said to her, and it's a lie, and they both let it be one. She takes his cock out and takes him in her mouth, and the cold boss makes a sound like something giving way under load.</p><span id="cascade-b2-beat-1"><<linkreplace "Work him.">><p>She works him slow, the way she's learned men like him want it — not skilled exactly, but devoted, like he's the only thing in the room worth her mouth. He finishes with his fist in her hair and the false name she's working under coming out of him broken in half. She swallows, and clocks the shape of what just cracked in him.</p>[[Wipe your mouth, get back to work.->Location_renner_depot]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_renner_blowjob">>
<<set $game_state.current_node = "cap_renner_blowjob.base">>
<<script>>setup.markCanvasTriggered("cap_renner_blowjob");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_renner_blowjob.base") === -1) { vn.push("cap_renner_blowjob.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "renner_oral_once", "set");<</script>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["scenes_rung_renner_grope_t4"] to ($game_state.media_cycle["scenes_rung_renner_grope_t4"] === undefined ? 0 : ($game_state.media_cycle["scenes_rung_renner_grope_t4"] + 1) % 4)>><<set _mc to $game_state.media_cycle["scenes_rung_renner_grope_t4"]>><<if _mc is 0>><video src="./videos/scenes/rung_renner_grope_t4/c82aa36e4dc.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/scenes/rung_renner_grope_t4/ca26f3feffe.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/scenes/rung_renner_grope_t4/cf090876321.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/scenes/rung_renner_grope_t4/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She stops pretending the contact is accidental — a hand on his thigh while he signs something, sliding up, letting him feel that she means it.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 30}]})>>
<p>He goes still. The pen stops. For a second the gutted business and the blacklist and the drinking are gone and there's just her hand and his breathing.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...don't stop.</div></div><p>Then he catches himself and pulls back, and hates that he had to.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 30}]})>>
<p>He doesn't pretend anymore. He covers her hand with his and presses it harder against him, his head going back, the cold boss just a man in a chair letting the help take him apart.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Whatever this is. Don't — don't stop doing it.</div></div>
<</if>>
<<set $game_state.current_canvas = "rung_renner_grope">>
<<set $game_state.current_node = "rung_renner_grope.base">>
<<script>>setup.markCanvasTriggered("rung_renner_grope");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_renner_grope.base") === -1) { vn.push("rung_renner_grope.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
[[Back to the invoices.->Location_renner_depot]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["scenes_rung_renner_flash_t3"] to ($game_state.media_cycle["scenes_rung_renner_flash_t3"] === undefined ? 0 : ($game_state.media_cycle["scenes_rung_renner_flash_t3"] + 1) % 4)>><<set _mc to $game_state.media_cycle["scenes_rung_renner_flash_t3"]>><<if _mc is 0>><video src="./videos/scenes/rung_renner_flash_t3/c3e9b103be9.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/scenes/rung_renner_flash_t3/c0814f3a987.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/scenes/rung_renner_flash_t3/cd2f90ecdd8.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/scenes/rung_renner_flash_t3/cee231736f1.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She's wearing nothing under the coveralls today, and she makes sure the moment comes where he can't not see it — bending slow for a low crate while he's looking.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 20}]})>>
<p>He looks. He looks longer than a man pretending not to. Then he turns hard to the window.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Get back to work.</div></div><p>His voice has gone rough. He doesn't mean the window.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 20}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Christ. Come here.</div></div><p>He says it low, half to himself, and doesn't finish the order — doesn't know what he'd do with her if she came. The wanting's ahead of the nerve. But it's there now, plain, and they both felt it land.</p>
<</if>>
<<set $game_state.current_canvas = "rung_renner_flash">>
<<set $game_state.current_node = "rung_renner_flash.base">>
<<script>>setup.markCanvasTriggered("rung_renner_flash");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_renner_flash.base") === -1) { vn.push("rung_renner_flash.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
[[Back to the invoices.->Location_renner_depot]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["scenes_rung_renner_tease_t2"] to ($game_state.media_cycle["scenes_rung_renner_tease_t2"] === undefined ? 0 : ($game_state.media_cycle["scenes_rung_renner_tease_t2"] + 1) % 4)>><<set _mc to $game_state.media_cycle["scenes_rung_renner_tease_t2"]>><<if _mc is 0>><video src="./videos/scenes/rung_renner_tease_t2/c8e0b66b889.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/scenes/rung_renner_tease_t2/c78be8006e3.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/scenes/rung_renner_tease_t2/ce10b88bda5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/scenes/rung_renner_tease_t2/cf1e91bb982.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>In the office now, the door shut. She works close — leans across him for a file she doesn't need, lets her body say the thing her mouth won't.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "lt", "value": 10}]})>>
<div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Filing's in the cabinet. Not on my desk.</div></div><p>He doesn't look up. But he moves his chair back an inch, and the inch costs him something.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 10}]})>>
<p>His eyes go to her and snap away, and a muscle works in his jaw.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...You do that on purpose.</div></div><p>It isn't quite a question, and he doesn't tell her to stop.</p>
<</if>>
<<set $game_state.current_canvas = "rung_renner_tease">>
<<set $game_state.current_node = "rung_renner_tease.base">>
<<script>>setup.markCanvasTriggered("rung_renner_tease");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("rung_renner_tease.base") === -1) { vn.push("rung_renner_tease.base"); }<</script>>
<<script>>advanceTime(180);<</script>>
[[Back to the invoices.->Location_renner_depot]]<img src="./videos/scenes/renner_office.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>It takes a week of good work before he stops finding fault just to have something to say to her. Then one grey afternoon he jerks his head at the glassed-in box at the back of the yard — his office — and doesn't quite look at her when he says it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Invoices are three months behind and I can't read my own writing. You'll do them in here. Heater works, door shuts, and I don't have to watch the help breathe.</div></div><p>It's the closest thing to trust a man this gutted has left to give. The door clicks shut behind her, and the public yard with its cameras and its cold becomes, for the length of a workday, a small private room with one wrecked man in it. Everything she's here to do starts now.</p>
<<set $game_state.current_canvas = "cap_renner_noticed">>
<<set $game_state.current_node = "cap_renner_noticed.base">>
<<script>>setup.markCanvasTriggered("cap_renner_noticed");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_renner_noticed.base") === -1) { vn.push("cap_renner_noticed.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "renner_office_open", "set");<</script>>
[[Get to work.->Location_renner_depot]]<<set _sub_target = setup.checkAndSubstituteCanvas("hub_anchor_renner")>><<if _sub_target>><<goto _sub_target>><</if>>
<img src="./videos/scenes/renner_anchor_evening.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Renner's at his end of the bar, working on the evening's ruin. Off the clock he's quieter and worse — less temper, more rot. He tolerates her sitting near him now, which is its own kind of progress.</p>
<<set $game_state.current_canvas = "hub_anchor_renner">>
<<set $game_state.current_node = "hub_anchor_renner.base">>
<<script>>setup.markCanvasTriggered("hub_anchor_renner");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_anchor_renner.base") === -1) { vn.push("hub_anchor_renner.base"); }<</script>>
<<nobr>>
<<link "Sit with him a while." "Canvas_hub_anchor_renner_Node_sit">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Leave." "Location_the_waterfront">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She sits. He doesn't talk and doesn't tell her to go. The bartender refills him without being asked. Somewhere in the quiet she clocks what she sees — the blacklist letters he doesn't hide anymore, the way his hands shake until the second drink — and lets him think it's company.</p>
<<set $game_state.current_canvas = "hub_anchor_renner">>
<<set $game_state.current_node = "hub_anchor_renner.sit">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_anchor_renner.sit") === -1) { vn.push("hub_anchor_renner.sit"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Head out.->Location_the_anchor]]<<set _costs to [{"trait": "energy", "value": 15}]>>\
<<if setup.checkCostsAffordable(_costs)>>\
<<script>>setup.deductCosts("work_depot_haul");<</script>>
<<set _sub_target = setup.checkAndSubstituteCanvas("work_depot_haul")>><<if _sub_target>><<goto _sub_target>><</if>>
<img src="./videos/activities/renner_menial.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She works the floor — hauling, sorting, logging crates against a manifest that hasn't matched reality since Cain came through. It's dull, and her body doesn't tire the way a real one would, so she's better at it than the last three hands he ran off. The pay is thin, counted out grudging. But she does it well, and well is a language even a man this far gone still reads.</p>
<<set $game_state.current_canvas = "work_depot_haul">>
<<set $game_state.current_node = "work_depot_haul.base">>
<<script>>setup.markCanvasTriggered("work_depot_haul");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("work_depot_haul.base") === -1) { vn.push("work_depot_haul.base"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_renner", "relation", "add", 3.0, false, null);setup.applyAndNotifyTrait("player", null, "money", "add", 8.0, false, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "worked_renner_once", "set");<</script>>
[[Knock off for now.->Location_renner_depot]]
<<else>>
<div class="cost-blocked-message">
<p><<= setup.getCostBlockedMessage(_costs)>></p>
</div>
[[Back->Location_renner_depot]]
<</if>><<set _sub_target = setup.checkAndSubstituteCanvas("hub_depot_floor")>><<if _sub_target>><<goto _sub_target>><</if>>
<img src="./videos/scenes/renner_depot_floor.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Renner's depot. Half-empty racks, a forklift nobody's fixed, crates Cain's people left behind. He works the floor in a temper, and when he notices her at all it's to find fault. The work is the work.</p>
<<set $game_state.current_canvas = "hub_depot_floor">>
<<set $game_state.current_node = "hub_depot_floor.base">>
<<script>>setup.markCanvasTriggered("hub_depot_floor");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_depot_floor.base") === -1) { vn.push("hub_depot_floor.base"); }<</script>>
<<nobr>>
<<link "Check in with him." "Canvas_hub_depot_floor_Node_check_in">><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_office_open", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 0}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('hub_depot_floor:cc0') ? '' : 'unlocked-choice'">
<<link "Tease him." "Canvas_rung_renner_tease_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "corruption", "add", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "arousal", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_depot_floor:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Tease him.">Tease him. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<<else>>
<span class="locked-choice" title="Tease him.">Tease him.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_office_open", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 10}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('hub_depot_floor:cc1') ? '' : 'unlocked-choice'">
<<link "Flash him — work commando." "Canvas_rung_renner_flash_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "corruption", "add", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "arousal", "add", 4.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "renner_flirts_back", "set");<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_depot_floor:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Flash him — work commando.">Flash him — work commando. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<<else>>
<span class="locked-choice" title="Flash him — work commando.">Flash him — work commando.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_office_open", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "renner_flirts_back", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 20}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('hub_depot_floor:cc2') ? '' : 'unlocked-choice'">
<<link "Get your hands on him." "Canvas_rung_renner_grope_Node_base">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "corruption", "add", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_renner", "arousal", "add", 5.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_depot_floor:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Get your hands on him.">Get your hands on him. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<<else>>
<span class="locked-choice" title="Get your hands on him.">Get your hands on him.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "renner_office_open", "operator": "is_true"}, {"type": "flag", "subject": "player", "flag_key": "renner_oral_once", "operator": "is_true"}, {"type": "trait", "subject": "npc", "npc_id": "npc_renner", "trait_key": "corruption", "operator": "gte", "value": 40}]})>>
<span @class="setup.isChoiceVisited('hub_depot_floor:cc3') ? '' : 'unlocked-choice'">
<<link "Take him in the office." "Canvas_loop_renner_office_sex_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "renner_fucked_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("hub_depot_floor:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Take him in the office.">Take him in the office.</span><br>
<</if>>
<<link "Head out." "Location_the_waterfront">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She checks in. He grunts something about the manifest and doesn't look up. It's not much — but she lets him not-look, lets him get used to the shape of her in his yard. That's the work under the work.</p>
<<set $game_state.current_canvas = "hub_depot_floor">>
<<set $game_state.current_node = "hub_depot_floor.check_in">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("hub_depot_floor.check_in") === -1) { vn.push("hub_depot_floor.check_in"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Back to it.->Location_renner_depot]]<img src="./videos/scenes/renner_anchor.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The Anchor is the kind of bar that's given up on its own neon. Renner's at the end of it — a big man gone soft and sour, three drinks into the evening and looking like the fourth won't help. He doesn't look up when she takes the stool beside him.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> Whatever you're selling, I'm broke. Whatever you're offering, I've had it.</div></div><p>She tells him she heard he's rebuilding. That she works cheap, doesn't ask questions, doesn't tire. He looks at her then — not the way men look, the way a man counting his last dollars looks at a price that's almost low enough.</p><span id="cascade-b2-beat-1"><<linkreplace "Wait.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/renner.jpg" alt="Renner" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Renner:</strong> ...Depot. Nine sharp. You're late, you're gone. You steal, I break your hands. Now buy your own drink or get off my stool.</div></div><p>He goes back to the glass. It isn't much. But she's in — and being in, day after day, until a man like this stops noticing she's there, is the whole way the job gets done.</p>[[Leave him to it.->Location_the_anchor]]
<</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_renner_hired">>
<<set $game_state.current_node = "cap_renner_hired.base">>
<<script>>setup.markCanvasTriggered("cap_renner_hired");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_renner_hired.base") === -1) { vn.push("cap_renner_hired.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "renner_hired", "set");<</script>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<p>He pulls out and finishes on her face, holding her there by the jaw until he's done, then letting go the way you drop a thing back in a drawer. He's already turned to the window.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_finish_facial_t5"] to ($game_state.media_cycle["sex_mercer_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_finish_facial_t5/c781f4d92a8.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_finish_facial_t5/c66861dfacf.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_finish_facial_t5/ce54fec1275.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_finish_facial_t5/incumbent.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<p>He finishes inside her, holds, pulls out without comment. His cum slides down the inside of her thigh. He wipes himself on a cloth from the drawer and checks the time.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_finish_inside_t5"] to ($game_state.media_cycle["sex_mercer_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_finish_inside_t5/c1bd134c597.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_finish_inside_t5/c152e84f955.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_finish_inside_t5/c306a472344.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_finish_inside_t5/c2090f3207c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<p>He finishes in her ass — and nothing comes. No give, no opening, no man-belonging-to-her. Just his weight and his cum and then his absence as he steps back. The same nothing as every time. She sets it aside with the other things she isn't allowed to ask, and straightens up.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_finish_ass_t5"] to ($game_state.media_cycle["sex_mercer_finish_ass_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_finish_ass_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_finish_ass_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_finish_ass_t5/c01e4903748.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_finish_ass_t5/c1003e30c7b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_finish_ass_t5/c20597f54be.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_finish_ass_t5/c59293fd397.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>>
<<set $game_state.current_canvas = "mercer_finisher">>
<<set $game_state.current_node = "mercer_finisher.climax">>
<<script>>setup.markCanvasTriggered("mercer_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_finisher.climax") === -1) { vn.push("mercer_finisher.climax"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Straighten up.->Location_penthouse]]<p>He's already unbuckling when she crosses to him; there's never preamble. He uses her the way he uses anything he owns — fully, without thanks, his mind somewhere else the whole time.</p>
<<set $game_state.current_canvas = "mercer_serve">>
<<set $game_state.current_node = "mercer_serve.intro">>
<<script>>setup.markCanvasTriggered("mercer_serve");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_serve.intro") === -1) { vn.push("mercer_serve.intro"); }<</script>>
<<nobr>>
<<link "Down on your knees." "Canvas_mercer_serve_Node_base_knees">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_serve_knees_t5"] to ($game_state.media_cycle["sex_mercer_serve_knees_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_serve_knees_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_serve_knees_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_serve_knees_t5/c0c758f63e7.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_serve_knees_t5/c4415c30da2.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_serve_knees_t5/ca6b08d8802.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_serve_knees_t5/cffcdcc1184.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees on the floor, his cock in her mouth, his hand setting the pace by her hair. He watches the city and not her, talking to no one — a maintenance note, a time he wants a thing done. She services him.</p>
<<set $game_state.current_canvas = "mercer_serve">>
<<set $game_state.current_node = "mercer_serve.base_knees">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_serve.base_knees") === -1) { vn.push("mercer_serve.base_knees"); }<</script>>
<<nobr>>
<<link "Keep sucking his cock." "Canvas_mercer_serve_Node_base_knees">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him bend you over the desk." "Canvas_mercer_serve_Node_base_desk">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Present your ass against the glass." "Canvas_mercer_serve_Node_base_glass">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('mercer_serve:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("mercer_serve:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_serve_desk_t5"] to ($game_state.media_cycle["sex_mercer_serve_desk_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_serve_desk_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_serve_desk_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_serve_desk_t5/c9b1f3f6b50.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_serve_desk_t5/c43e89f51dc.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_serve_desk_t5/cfcf7381033.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_serve_desk_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent over the desk, his hand flat between her shoulder blades to keep her down, he fucks her from behind — unhurried, proprietary. Her body answers the way it always does, and he notes that it does, the way you'd note a gauge reading true.</p>
<<set $game_state.current_canvas = "mercer_serve">>
<<set $game_state.current_node = "mercer_serve.base_desk">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_serve.base_desk") === -1) { vn.push("mercer_serve.base_desk"); }<</script>>
<<nobr>>
<<link "Take it." "Canvas_mercer_serve_Node_base_desk">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_mercer_serve_Node_base_knees">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him take your ass." "Canvas_mercer_serve_Node_base_glass">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('mercer_serve:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("mercer_serve:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_mercer_serve_glass_t5"] to ($game_state.media_cycle["sex_mercer_serve_glass_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_mercer_serve_glass_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_mercer_serve_glass_t5"]>><<if _mc is 0>><video src="./videos/sex/mercer_serve_glass_t5/c2e775e29e9.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/mercer_serve_glass_t5/cc32ed19646.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/mercer_serve_glass_t5/cee6a58b71d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/mercer_serve_glass_t5/incumbent.webm" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Against the glass wall, the lit city laid out under them, he takes her ass in long deliberate strokes, both hands on her hips. This is the act that hands her the controls of any other man alive. She braces on the glass and waits to feel it start — the give, the opening. It doesn't come. It never comes with him.</p>
<<set $game_state.current_canvas = "mercer_serve">>
<<set $game_state.current_node = "mercer_serve.base_glass">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_serve.base_glass") === -1) { vn.push("mercer_serve.base_glass"); }<</script>>
<<nobr>>
<<link "Take it in the ass." "Canvas_mercer_serve_Node_base_glass">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back over the desk." "Canvas_mercer_serve_Node_base_desk">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('mercer_serve:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass." "Canvas_mercer_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("mercer_serve:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><img src="./videos/scenes/mercer_penthouse_hub.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Mercer's penthouse. He's at the desk or the glass, and whatever he's doing he can be interrupted to use her — that's what she's for, and they both know it. He looks up when she comes in. He doesn't smile. He never smiles.</p>
<<set $game_state.current_canvas = "mercer_penthouse_hub">>
<<set $game_state.current_node = "mercer_penthouse_hub.base">>
<<script>>setup.markCanvasTriggered("mercer_penthouse_hub");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_penthouse_hub.base") === -1) { vn.push("mercer_penthouse_hub.base"); }<</script>>
<<nobr>>
<<link "Report." "Canvas_mercer_penthouse_hub_Node_report">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Kneel and serve him." "Canvas_mercer_serve_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Leave." "Location_atrium">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She gives him what she has — the Renner approach, the angle, the timeline. He listens the way he listens to a status light: for whether it's green.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Don't bring me progress. Bring me the man's secrets or bring me nothing. Go make yourself useful to someone who can't see what you are.</div></div>
<<set $game_state.current_canvas = "mercer_penthouse_hub">>
<<set $game_state.current_node = "mercer_penthouse_hub.report">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("mercer_penthouse_hub.report") === -1) { vn.push("mercer_penthouse_hub.report"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Go.->Location_penthouse]]<img src="./videos/scenes/mercer_penthouse.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The penthouse, by daylight. Mercer's at the glass with a tablet, and he doesn't turn when the elevator lets her in. He doesn't have to. She's company-issue, and she's here because being here is what she's for.</p><span id="cascade-b2-beat-0"><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> On your knees. I've got a call in ten.</div></div><span id="cascade-b2-beat-1"><<linkreplace "Kneel.">><p>She goes down without a word. He takes the call one-handed, her mouth around his cock, the other hand resting in her hair the way a man rests his hand on a tool he isn't using yet. He finishes before the call does. She swallows, because that's tidy, and tidy is what's expected.</p><video src="./videos/sex/mercer_first_oral_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><span id="cascade-b2-beat-2"><<linkreplace "Hear the terms.">><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Good. You come up when I want you, and you're like this when you do. That's the arrangement. It was always the arrangement.</div></div><p>It was. She straightens, wipes her mouth, and the shape of the days settles over her — the same shape as all the days behind it: the cradle, the call, the kneeling. World without end.</p>[[Stand up.->Location_penthouse]]
<</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "cap_first_penthouse_service">>
<<set $game_state.current_node = "cap_first_penthouse_service.base">>
<<script>>setup.markCanvasTriggered("cap_first_penthouse_service");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cap_first_penthouse_service.base") === -1) { vn.push("cap_first_penthouse_service.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "mercer_hub_open", "set");<</script>><p>The bolt turns. One cubicle, one cracked basin, a hook on the back of the door, and the bar coming through the wall at the volume it comes through everything down here.</p>
<<set $game_state.current_canvas = "activity_bar_change">>
<<set $game_state.current_node = "activity_bar_change.base">>
<<script>>setup.markCanvasTriggered("activity_bar_change");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_bar_change.base") === -1) { vn.push("activity_bar_change.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "face_worn", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('activity_bar_change:cc0') ? '' : 'unlocked-choice'">
<<link "Change into the dress." "Canvas_activity_bar_change_Node_into">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.addToWardrobe("dress_undertow"); setup.equipItem("dress_undertow");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_bar_change:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Out of the face she is Wren, and Wren does not work here — Sol hired a stranger, and the dress belongs to the stranger. Put it on at the cot first.">Out of the face she is Wren, and Wren does not work here — Sol hired a stranger, and the dress belongs to the stranger. Put it on at the cot first.</span><br>
<</if>>
<<link "Back into your own things." "Canvas_activity_bar_change_Node_out">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.addToWardrobe("cover_stranger"); setup.equipItem("cover_stranger");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Leave it." "Location_underworld_bar_bathroom">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>It goes on over the head and is the right size, which she has stopped finding interesting and has not stopped noticing. Her own things fold small enough to sit on the hook under her coat.</p>
<<set $game_state.current_canvas = "activity_bar_change">>
<<set $game_state.current_node = "activity_bar_change.into">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_bar_change.into") === -1) { vn.push("activity_bar_change.into"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Out onto the floor.->Location_underworld_bar_bathroom]]<p>The dress goes on the hook where the bag was and stays in the building, because it is not hers and it has never once left with her. Her own kit smells of the strip and of the cot and of nothing anybody chose for her.</p>
<<set $game_state.current_canvas = "activity_bar_change">>
<<set $game_state.current_node = "activity_bar_change.out">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_bar_change.out") === -1) { vn.push("activity_bar_change.out"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Out.->Location_underworld_bar_bathroom]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "face_worn", "operator": "is_false"}]})>>
<p>The bag is under the cot where it lives. Twenty minutes at the cracked glass over the basin and she is somebody else — she has the order of it now, and her hands do most of it without being asked.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "face_worn", "operator": "is_true"}]})>>
<p>She is wearing it now, in her own room, with nobody to be it at. It comes off in about a minute and goes back on in twenty, which is the exchange rate on being nobody.</p>
<</if>>
<<set $game_state.current_canvas = "activity_the_face">>
<<set $game_state.current_node = "activity_the_face.base">>
<<script>>setup.markCanvasTriggered("activity_the_face");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_the_face.base") === -1) { vn.push("activity_the_face.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "face_worn", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('activity_the_face:cc0') ? '' : 'unlocked-choice'">
<<link "Put it on." "Canvas_activity_the_face_Node_on">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "face_worn", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_the_face:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "face_worn", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('activity_the_face:cc1') ? '' : 'unlocked-choice'">
<<link "Take it off." "Canvas_activity_the_face_Node_off">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "face_worn", "unset");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_the_face:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave it." "Location_the_cot">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The order of it is fixed now: the hairline first, then the jaw, then the small dull work around the eyes that does more than any of the rest. She does not look at the whole of it until the end.</p><p>The woman in the glass has a job at a bar off the strip, no history anybody has asked about, and nothing at all to do with a programme that closed eleven years ago.</p>
<<set $game_state.current_canvas = "activity_the_face">>
<<set $game_state.current_node = "activity_the_face.on">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_the_face.on") === -1) { vn.push("activity_the_face.on"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Go to work.->Location_the_cot]]<p>It comes off faster than it goes on. A minute at the basin, the water going grey, and the woman who has been carrying trays for a man who owned her stops existing over a cracked sink in a ship-breaker's yard.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Nothing out there is hers while this is off. Sol does not know her. The floor is not her floor and the rota does not have her on it, and the room behind the door is a room she has never been invited into. All of that belongs to a woman with no name who does not exist until she puts it back on.</div></div><p>She does it anyway, most nights, for an hour, and could not give a reason that would survive being said out loud.</p><p>The face in the glass is four years older than the paperwork says and has a designation on a page in a file under the cot, and it is the only thing in this city that nobody sold her.</p>
<<set $game_state.current_canvas = "activity_the_face">>
<<set $game_state.current_node = "activity_the_face.off">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_the_face.off") === -1) { vn.push("activity_the_face.off"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Sit with it a while.->Location_the_cot]]<img src="./videos/scenes/backroom_ruin.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The Undertow does not open in the evenings any more. Sol is in it most days with a shovel and a bucket and no particular plan, and he lets her past the tape because she is the only one of the staff who came back at all.</p><p>The back door is off its frame. What is behind it is a burned floor two inches deep in other people's paperwork, and most of it is worth exactly what it looks like.</p>
<<set $game_state.current_canvas = "activity_sift_the_ruin">>
<<set $game_state.current_node = "activity_sift_the_ruin.base">>
<<script>>setup.markCanvasTriggered("activity_sift_the_ruin");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_sift_the_ruin.base") === -1) { vn.push("activity_sift_the_ruin.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 10}])>>
<<link "Sift it. (An hour on your knees.)" "Canvas_activity_sift_the_ruin_Node_sift">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "ruin_depth", "add", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -10, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Sift it. (An hour on your knees.)">Sift it. (An hour on your knees.) (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 10}])>>)</span><br>
<</if>>
<<link "Leave it for today." "Location_underworld_bar">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "ruin_depth", "operator": "lt", "value": 2}]})>>
<p>An hour on her knees in wet ash. Manifests, bills of lading, a decade of somebody moving cargo that was never cargo. All of it worthless and all of it his, and none of it what he was actually keeping.</p><p>The spines with the dates on them are gone. Not burned — gone. Somebody lifted whole years off that wall before the fire and the fire was for the rest.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "ruin_depth", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "ruin_depth", "operator": "lt", "value": 4}]})>>
<p>Under the desk, where the desk protected it, half a folder with the top third burned off. Vance paper. A transfer of custody, eleven years old, and a designation in the same format as her own.</p><p>It is not hers. She checks it four times against a number she has read exactly once and will never need to read again. The second date column on that sheet is filled in.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "ruin_depth", "operator": "gte", "value": 4}]})>>
<p>She goes back through it in daylight, and then again, and what she has at the end of it is nine sheets that mean anything and a coat that smells of burned paper for a fortnight.</p><p>Nine designations in her own format. Six of them have a date in the second column. Two do not. One of them is a line she has read before, on a bench, at a breaking dock, with a man standing over there pretending not to look.</p><p>There is nothing in any of it about where a man might live, and nothing in any of it about who came back eight months after the programme closed and sealed one page. That is not in this room. That was never going to be in this room.</p>
<</if>>
<<set $game_state.current_canvas = "activity_sift_the_ruin">>
<<set $game_state.current_node = "activity_sift_the_ruin.sift">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_sift_the_ruin.sift") === -1) { vn.push("activity_sift_the_ruin.sift"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Take what there is.->Location_underworld_bar]]<img src="./videos/locations/underworld_market.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The blade tables want forty and she has stood at them twice and walked off twice, because a knife in that room is a thing she would have to be willing to use on a man she needs conscious.</p><p>What she is actually short of is the emitter, and the emitter is not an option. One core feeds one weapon and the drain has to be the one that is live, every night she goes in — and a Vance field unit in her coat would come out of it at the door, in front of the room, in a man's hand.</p><p>So: the box at the end of the row with the cheap gear in it, where a hand-taser is thirty coin and every woman working a floor down here has one.</p>
<<set $game_state.current_canvas = "activity_buy_taser">>
<<set $game_state.current_node = "activity_buy_taser.base">>
<<script>>setup.markCanvasTriggered("activity_buy_taser");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_taser.base") === -1) { vn.push("activity_buy_taser.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 30}])>>
<<link "Buy the taser. (30 coin.)" "Canvas_activity_buy_taser_Node_buy">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "taser_held", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -30, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Buy the taser. (30 coin.)">Buy the taser. (30 coin.) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 30}])>>)</span><br>
<</if>>
<<link "Leave it." "Location_underworld_market">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>It is a grey plastic thing the size of her palm with a thumb-switch and a charge light that does not work. The man selling it does not ask what it is for, because down here nobody has ever had to explain that one.</p><p>It goes in the coat, in the lining, where he is going to look.</p>
<<set $game_state.current_canvas = "activity_buy_taser">>
<<set $game_state.current_node = "activity_buy_taser.buy">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_taser.buy") === -1) { vn.push("activity_buy_taser.buy"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Take it to work.->Location_underworld_market]]<img src="./videos/locations/underworld_bar.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 2}]})>>
<p>Glasses, tables, the three steps with a full tray. Nobody looks at her and nobody speaks to her except to want something, which after a life of being the most interesting thing in every room she is in is close to restful.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 3}]})>>
<p>Behind the taps now, which is a different job entirely. Everything said in this room gets said at the counter eventually, and Sol has spent thirty years standing here hearing all of it and repeating none.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 3}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 4}]})>>
<p>Back on the floor, in what came down from the back room in a bag with no note. Sol handed it over without a word and without meeting her eye, which told her whose bag it was more clearly than a note would have.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 4}]})>>
<p>The floor, the tray, the three steps, same as the first night. The only thing that has changed is that the door at the back opens for her now, and every man in this room who has noticed that has stopped looking at her twice.</p>
<</if>>
<<set $game_state.current_canvas = "activity_bar_work">>
<<set $game_state.current_node = "activity_bar_work.base">>
<<script>>setup.markCanvasTriggered("activity_bar_work");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_bar_work.base") === -1) { vn.push("activity_bar_work.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 2}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_bar_work:cc0') ? '' : 'unlocked-choice'">
<<link "Work the floor. (15 coin.)" "Canvas_activity_bar_work_Node_shift">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_bar_work:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work the floor. (15 coin.)">Work the floor. (15 coin.) (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 3}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_bar_work:cc1') ? '' : 'unlocked-choice'">
<<link "Work the counter. (15 coin.)" "Canvas_activity_bar_work_Node_shift">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_bar_work:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work the counter. (15 coin.)">Work the counter. (15 coin.) (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 3}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 4}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_bar_work:cc2') ? '' : 'unlocked-choice'">
<<link "Work the floor in what he sent down. (15 coin.)" "Canvas_activity_bar_work_Node_shift">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_bar_work:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work the floor in what he sent down. (15 coin.)">Work the floor in what he sent down. (15 coin.) (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 4}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_bar_work:cc3') ? '' : 'unlocked-choice'">
<<link "Work the floor. (15 coin.)" "Canvas_activity_bar_work_Node_shift">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 15.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_bar_work:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work the floor. (15 coin.)">Work the floor. (15 coin.) (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<link "Not tonight." "Location_underworld_bar">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 2}]})>>
<p>Six hours of it. Her hands go raw in the water and her back finds the exact place three steps with a loaded tray puts the weight, and she learns the room the way you only learn a room by carrying things across it.</p><p>Twice in the night the back door opens and closes and she does not turn round, because the girls who turn round are the ones Sol talks to afterwards.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 3}]})>>
<p>The taps, the till, and the whole room's talk arriving at her elbow in pieces. Most of it is nothing. She keeps the pieces that are about the back door and there are more of them than she expected.</p><p>Men go in with envelopes and come out without them. Nobody carries anything out. Whatever that room is, it takes paper in one direction only, and it has been doing it for years.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 3}, {"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "lt", "value": 4}]})>>
<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_bar_floor_handled_t4"] to ($game_state.media_cycle["sex_bar_floor_handled_t4"] === undefined ? 0 : ($game_state.media_cycle["sex_bar_floor_handled_t4"] + 1) % 3)>><<set _mc to $game_state.media_cycle["sex_bar_floor_handled_t4"]>><<if _mc is 0>><video src="./videos/sex/bar_floor_handled_t4/bar_floor_handled_t4a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/bar_floor_handled_t4/c7b2257f0d0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/bar_floor_handled_t4/c4583743a42.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>The dress is short and the material is thin and it was cut for a woman standing still, which is not what the job is. Bending for the low tables is the whole of the arithmetic and everyone in the room has done it.</p><p>Hands land. Not often and never hard — a palm flat on her ass while she reaches across, a hand up under the hem that finds her cunt through nothing at all and is gone before she straightens, knuckles dragging her tits through the thin material as she leans past. She sets the glasses down and moves on and it costs her nothing, and that is the part she notices.</p><p>Nobody goes further than that. They look at the back door first, every one of them, and then they take their hand back.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "bar_rung", "operator": "gte", "value": 4}]})>>
<p>Nobody's hand goes anywhere near her now. The dress is the same dress and the tables are the same height, and the whole room has quietly agreed she is not theirs to touch any more.</p><p>It is not respect. It is the same instinct that makes them all check the back door first, arriving one step earlier.</p>
<</if>>
<<set $game_state.current_canvas = "activity_bar_work">>
<<set $game_state.current_node = "activity_bar_work.shift">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_bar_work.shift") === -1) { vn.push("activity_bar_work.shift"); }<</script>>
<<script>>advanceTime(240);<</script>>
[[Close out.->Location_underworld_bar]]<img src="./videos/locations/underworld_market.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Past the blades, past the drawers of dead components, past the pens. The last three tables are curtained on three sides and the woman behind them does not sell anything you can hold up to the light.</p><p>She deals in the paperwork of being somebody — a face, a history thin enough not to check and thick enough not to ask about, and the clothes that make a stranger believe both without being told.</p><p>The price is the price. She does not haggle and she does not explain what any of it costs her to arrange.</p>
<<set $game_state.current_canvas = "activity_buy_face">>
<<set $game_state.current_node = "activity_buy_face.base">>
<<script>>setup.markCanvasTriggered("activity_buy_face");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_face.base") === -1) { vn.push("activity_buy_face.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 120}])>>
<<link "Buy the face. (120 coin.)" "Canvas_activity_buy_face_Node_buy">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "face_bought", "set");<</script>><<script>>setup.addToWardrobe("cover_stranger");<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -120, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Buy the face. (120 coin.)">Buy the face. (120 coin.) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 120}])>>)</span><br>
<</if>>
<<link "Not yet." "Location_underworld_market">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>It takes the afternoon and most of it is sitting still. The woman works close, without conversation, the way Kess works, and asks her exactly one question, which is whether anyone has ever taken a print of her.</p><p>She says no. It is the first lie the face tells and it is told before the face exists.</p><p>What comes out at the end is not a mask and does not feel like one. It is a set of small differences that add up to somebody else, and a bag of clothes that belong to the somebody else rather than to her.</p><p>"Don't wear it in front of anyone who knew the old one," the woman says, folding the bag shut. "That's not me being careful. That's the whole of what you paid for."</p>
<<set $game_state.current_canvas = "activity_buy_face">>
<<set $game_state.current_node = "activity_buy_face.buy">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_face.buy") === -1) { vn.push("activity_buy_face.buy"); }<</script>>
<<script>>advanceTime(90);<</script>>
[[Take it back to the berth.->Location_underworld_market]]<p>There is nothing in the room to do. There are three things in it to notice.</p>
<<set $game_state.current_canvas = "cell_attend">>
<<set $game_state.current_node = "cell_attend.base">>
<<script>>setup.markCanvasTriggered("cell_attend");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_attend.base") === -1) { vn.push("cell_attend.base"); }<</script>>
<<nobr>>
<<link "Listen at the door." "Canvas_cell_attend_Node_listen">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Watch the man on the chair." "Canvas_cell_attend_Node_watch">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Hold still and feel the fault." "Canvas_cell_attend_Node_hold_still">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>Steel carries. She sits with her shoulder against it and counts: five voices, six, one that comes and goes and never finishes a sentence because the others stop talking when it does. Cards, some nights. A radio. A long argument about a boat that she thinks is not about a boat.</p><p>Nothing about her. That's the part she keeps turning over. Whatever she is to them, she isn't a thing they discuss.</p>
<<set $game_state.current_canvas = "cell_attend">>
<<set $game_state.current_node = "cell_attend.listen">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_attend.listen") === -1) { vn.push("cell_attend.listen"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Sit back.->Location_captive_room]]<p>There's always one, and it's always the chair. He doesn't talk to her. He reads, or he doesn't, and he watches the way a man watches a pot. When she looks back at him he holds it, bored, and eventually she is the one who looks away, and she notes that too, because a month ago she would not have.</p><p>Sometimes it's Bastien in the chair. He never reads. He sits with his hands still and studies her the way he studied her across the table, patient, waiting for the answer to a question he has not asked again.</p>
<<set $game_state.current_canvas = "cell_attend">>
<<set $game_state.current_node = "cell_attend.watch">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_attend.watch") === -1) { vn.push("cell_attend.watch"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Look away.->Location_captive_room]]<p>She lies on her back and stops doing anything at all, and listens to herself the way she listened to the door. The charge sits under her sternum with nowhere to be. It has a sound. It has, she realises, started to have a smell — hot dust, the inside of a machine that has been run too long.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "glitch_iii_seen", "operator": "is_false"}]})>>
<p>And then the fault opens, the way a badly-set bone opens, and something comes up through it that was never filed anywhere she can reach.</p><p>A hand at the back of her neck. Not holding her — resting there, the thumb moving once, slowly, along the seam. Somebody standing behind her while she looks at something she cannot see, and in the whole of her body, in the whole of the record, no fear at all. Not permitted absence of fear. The other kind. The kind that is simply not there because there is nothing in the room that could hurt her.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> Whose hand.</div></div><p>It closes. It goes back down wherever it lives. She lies in a room she cannot leave with men on the other side of the door and she finds that she is holding the back of her own neck, and that her eyes have gone wet, and that she has no idea, none, whose hand that was.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "flag", "subject": "player", "flag_key": "glitch_iii_seen", "operator": "is_true"}]})>>
<p>She reaches for it again — the hand, the seam, the quiet — and it isn't there. The fault doesn't open twice for the same thing. What's left is the pressure, and the hot-dust smell, and the certainty that it is worse than it was.</p>
<</if>>
<<set $game_state.current_canvas = "cell_attend">>
<<set $game_state.current_node = "cell_attend.hold_still">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_attend.hold_still") === -1) { vn.push("cell_attend.hold_still"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "glitch_iii_seen", "set");<</script>>
[[Let it go.->Location_captive_room]]<p>The light doesn't go off, so she turns her face to the wall and powers down anyway. The bed is bolted down and the mattress smells of other people. She goes under fast, the way she always does, and it doesn't matter at all, because the door opens in the night. It always opens in the night.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "lt", "value": 24}]})>>
<video src="./videos/sex/cell_sleep_nominal_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She's down on the mat with her back to the wall, trying to drop under, when the weight settles in behind her and Bastien starts using her before she's all the way awake — cock worked into her cunt from behind, unhurried, one hand laid flat on the sternum plate so he can feel it tick while he does it. The charge climbs with every slow shove and runs down to the empty socket at the base of her spine and stops there, nowhere to go, stacking behind the plate in the dark.</p><p>He finishes inside her and the read-out clicks up one notch and holds — it never drops — and he wipes off on the small of her back and leaves her where she lies. <em>Logged as rest. It isn't.</em></p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Don't mind me. Go back under, filthy thing — I'm just reading you.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> There it goes. One more on the count and nowhere for it to run. Bought that too.</div></div>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "gte", "value": 24}, {"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "lt", "value": 48}]})>>
<video src="./videos/sex/cell_sleep_hot_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>She surfaces because there are hands on her that aren't one man's — the crew have let themselves in in the dark, and she wakes already being used, a cock working her cunt while another feeds itself into her mouth and a third pair of hands just paws her tits like they're checking she's still warm. She can't tell how many; they pass her between them in the black, laughing low, and every load that goes in stacks up behind the plate with the drain gone and the socket empty, filling and filling with nowhere to send it.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Left the door open for the lads. She sleeps better with a hole full — don't you, slut.</div></div><p>"She's awake — watch, the little machine's awake."</p><p>"Doesn't matter if she is. Open, bought-and-paid-for."</p><p>"Pass her down, I've not had a turn."</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "gte", "value": 48}, {"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "lt", "value": 72}]})>>
<video src="./videos/sex/cell_sleep_faulting_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>They don't let her stay under at all now — every time she starts to slip they haul her back onto her knees and use her hard, one fisting her hair down onto his cock till she gags and drools, another driving into her from behind, handprints coming up on her ass and a set of fingermarks banding her throat. Then they flip her and take her in the ass — the drain inverted, the thing that used to TAKE from a man now only pouring INTO her — and Bastien holds her jaw and makes her come on it because forcing it spikes the read-out fastest, and the number jumps two, three, climbing in the dark while she can't tell sleep from being fucked.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Make her come — that's what moves the count, not the fucking. Force it. Watch the plate, not her.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Up it goes. Look at that. Passed round all night and still filling.</div></div><p>"She's shaking — do it again, it likes it."</p><p>"Hold her mouth, she's trying to check out."</p><p>"Mark her up, no one's counting bruises."</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "items": [{"type": "trait", "subject": "player", "trait_key": "core_strain", "operator": "gte", "value": 72}]})>>
<video src="./videos/sex/cell_sleep_failing_t5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><p>By the small hours her body's half-quitting under them — legs gone loose, breath sawing, the sternum plate flickering its read-out where it should hold steady — and none of them slow down for it. The charge has stacked up so high with nowhere to drain that every load now lands as a wet flare of pain instead of anything else, and they force her over the edge anyway, again, the pleasure coming out the wrong way and hurting, the plate stuttering the number up past where the light stays on. She's limp between them, used front and back, drooled-on and slack, and Bastien just leans in close to read the flicker off her chest and not her face at all.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> It's browning out. Good — keep going. I want to see how high it climbs before the light won't hold.</div></div><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/bastien.jpg" alt="Bastien" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Bastien:</strong> Not her, idiot — the plate. Watch the plate. She's just the socket.</div></div><p>"Something's wrong with it — look, it's flickering."</p><p>"Boss says keep going, so keep going."</p><p>"Still warm, still open. Who cares if it's breaking."</p>
<</if>>
<<set $game_state.current_canvas = "cell_sleep">>
<<set $game_state.current_node = "cell_sleep.base">>
<<script>>setup.markCanvasTriggered("cell_sleep");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("cell_sleep.base") === -1) { vn.push("cell_sleep.base"); }<</script>>
<<script>>advanceTime(540);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", 100.0, true, null);setup.applyAndNotifyTrait("player", null, "core_strain", "add", 12.0, true, null);setup.showEffectNotification();<</script>>
[[Wake up.->Location_captive_room]]<p>The locker at the foot of the bed holds everything the company has ever issued her and two things it hasn't. She stands in front of it with the door open and picks what the day needs her to look like.</p>
<<set $game_state.current_canvas = "activity_wren_change">>
<<set $game_state.current_node = "activity_wren_change.base">>
<<script>>setup.markCanvasTriggered("activity_wren_change");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wren_change.base") === -1) { vn.push("activity_wren_change.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "clothing_item", "item_id": "cover_dockhand", "operator": "owned"}]})>>
<span @class="setup.isChoiceVisited('activity_wren_change:cc0') ? '' : 'unlocked-choice'">
<<link "Into the dock-work coveralls." "Canvas_activity_wren_change_Node_dock">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.addToWardrobe("cover_dockhand"); setup.equipItem("cover_dockhand");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_wren_change:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="She hasn't been issued anything to work a dock in. Whatever the job turns out to be, it comes with the clothes.">She hasn't been issued anything to work a dock in. Whatever the job turns out to be, it comes with the clothes.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "clothing_item", "item_id": "cover_analyst", "operator": "owned"}]})>>
<span @class="setup.isChoiceVisited('activity_wren_change:cc1') ? '' : 'unlocked-choice'">
<<link "Into the junior-analyst kit." "Canvas_activity_wren_change_Node_analyst">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.addToWardrobe("cover_analyst"); setup.equipItem("cover_analyst");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_wren_change:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Nothing in the locker would get her past a desk on a Vance floor. That kit comes from Mercer, when Mercer wants her on one.">Nothing in the locker would get her past a desk on a Vance floor. That kit comes from Mercer, when Mercer wants her on one.</span><br>
<</if>>
<<link "Back into company grays." "Canvas_activity_wren_change_Node_grays">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.addToWardrobe("company_grays"); setup.equipItem("company_grays");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Leave it." "Location_wren_room">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>Heavy canvas, worn at the knees by somebody else first, and it smells of a shed. In the mirror there is a woman who lifts things for a living and no reason at all to look at her twice, which is the entire point of it.</p>
<<set $game_state.current_canvas = "activity_wren_change">>
<<set $game_state.current_node = "activity_wren_change.dock">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wren_change.dock") === -1) { vn.push("activity_wren_change.dock"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Out.->Location_wren_room]]<p>Grey, cut close, a size too junior to be worth resenting. It is the same trick as the coveralls run the other way — not a woman nobody looks at, a woman everybody has already decided they know.</p>
<<set $game_state.current_canvas = "activity_wren_change">>
<<set $game_state.current_node = "activity_wren_change.analyst">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wren_change.analyst") === -1) { vn.push("activity_wren_change.analyst"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Out.->Location_wren_room]]<p>The company line goes back on and sits the way it always sits, which is correctly. She has never once chosen these and has never once been out of them long enough to notice she wasn't.</p>
<<set $game_state.current_canvas = "activity_wren_change">>
<<set $game_state.current_node = "activity_wren_change.grays">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wren_change.grays") === -1) { vn.push("activity_wren_change.grays"); }<</script>>
<<script>>advanceTime(15);<</script>>
[[Out.->Location_wren_room]]<p>She strips and stands under the water until whatever the work left on her is gone — sweat, a client's hands, the smell of the docks. The company keeps her maintained; this part she does for herself, the one washing that isn't on anyone's schedule. She comes out clean, and for a minute, almost new.</p>
<<set $game_state.current_canvas = "activity_wash">>
<<set $game_state.current_node = "activity_wash.base">>
<<script>>setup.markCanvasTriggered("activity_wash");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_wash.base") === -1) { vn.push("activity_wash.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "hygiene", "add", 100.0, true, null);setup.showEffectNotification();<</script>>
[[Dry off. Dressed.->Location_wren_room]]<img src="./videos/locations/underworld_market.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Past the blades and the field-kit there are three tables nobody stops at, stacked with the insides of things: salvage boards, drawer after drawer of small dead components sorted by nothing. The man behind the tables doesn't look up until she says what she is after.</p><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "lt", "value": 1}]})>>
<p>A talkback. He doesn't blink at the word — just turns a drawer round and lets her pick out of forty of them, none of which he can tell apart either.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "lt", "value": 2}]})>>
<p>A sweep head. That gets a second look — sweeps come off signal boards, and nobody buys one to fix a signal board. He puts a flat grey wafer on the cloth and names a price without being asked.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "lt", "value": 3}]})>>
<p>An isolator. He tells her it's dead stock and nobody's asked for one in two years, which she already knew, because the man who sent her said the same sentence. It is heavier than it looks.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "part_spec", "operator": "gte", "value": 3}]})>>
<p>A stacked talkback. He goes into the back for this one. When he comes out he sets it down and looks at her for the first time in four visits, and decides against asking.</p>
<</if>>
<<set $game_state.current_canvas = "activity_buy_part">>
<<set $game_state.current_node = "activity_buy_part.base">>
<<script>>setup.markCanvasTriggered("activity_buy_part");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_part.base") === -1) { vn.push("activity_buy_part.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "spec_pending", "operator": "lt", "value": 1}]})>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 25}])>>
<span @class="setup.isChoiceVisited('activity_buy_part:cc0') ? '' : 'unlocked-choice'">
<<link "Buy a part. (25 coin.)" "Canvas_activity_buy_part_Node_buy">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "part_held", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -25, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_buy_part:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Buy a part. (25 coin.)">Buy a part. (25 coin.) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 25}])>>)</span><br>
<</if>>
<<else>>
<span class="locked-choice" title="The last one burned out in her and she has not been back to the berth since. Buying another before Kess has said what went wrong is how the first twenty-five went — he asks what it did, and then he says what to get.">The last one burned out in her and she has not been back to the berth since. Buying another before Kess has said what went wrong is how the first twenty-five went — he asks what it did, and then he says what to get.</span><br>
<</if>>
<<link "Leave the tables." "Location_underworld_market">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The man behind the drawers doesn't ask what it's for and doesn't look up while she pays, which is the only service on offer down here and the only one worth coin. It goes in her coat with the other thing she is not carrying openly.</p>
<<set $game_state.current_canvas = "activity_buy_part">>
<<set $game_state.current_node = "activity_buy_part.buy">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_part.buy") === -1) { vn.push("activity_buy_part.buy"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Take it to him.->Location_underworld_market]]<img src="./videos/locations/underworld_market.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Two tables past the blades a man does bonded freight — grey courier cases stacked to his shoulder, each with a reader plate let into the lid. Nothing down here moves without a hand on it at both ends.</p><div class="dialog-block dialog-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong><<print $player.name>>:</strong> It keeps the hand that opens it.</div></div><p>That's the whole product, he says. Anyone can carry a box. The case is how you prove who took delivery, and it holds that a year.</p>
<<set $game_state.current_canvas = "activity_buy_case">>
<<set $game_state.current_node = "activity_buy_case.base">>
<<script>>setup.markCanvasTriggered("activity_buy_case");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_case.base") === -1) { vn.push("activity_buy_case.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 25}])>>
<<link "Have him bond a bottle into one. (25 coin.)" "Canvas_activity_buy_case_Node_buy">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "case_held", "set", 1.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -25, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Have him bond a bottle into one. (25 coin.)">Have him bond a bottle into one. (25 coin.) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 25}])>>)</span><br>
<</if>>
<<link "Leave the table." "Location_underworld_market">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>He seats a bottle in the foam, drops the lid, and the reader plate goes dull and waiting. It is a good bottle. It is a better box, and only one of the two is going back to the Reach with her.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> He will open it in front of her. He will not be able to help it, and he will be pleased, and the empty will be sitting on the floor when she leaves.</div></div>
<<set $game_state.current_canvas = "activity_buy_case">>
<<set $game_state.current_node = "activity_buy_case.buy">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_buy_case.buy") === -1) { vn.push("activity_buy_case.buy"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Pocket it.->Location_underworld_market]]<img src="./videos/locations/underworld_market.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Coin moves here for everything the Spire pretends doesn't exist. Blades and field-gear on the near tables; further back, behind chain, the part she doesn't look at directly. She has coin now. She can leave with an edge.</p>
<<set $game_state.current_canvas = "underworld_market_shop">>
<<set $game_state.current_node = "underworld_market_shop.base">>
<<script>>setup.markCanvasTriggered("underworld_market_shop");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_market_shop.base") === -1) { vn.push("underworld_market_shop.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 40}])>>
<<link "Buy a weapon — something with an edge." "Canvas_underworld_market_shop_Node_weapon">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "fighting", "add", 5.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -40, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Buy a weapon — something with an edge.">Buy a weapon — something with an edge. (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 40}])>>)</span><br>
<</if>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 30}])>>
<<link "Buy infiltration gear." "Canvas_underworld_market_shop_Node_gear">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "stealth", "add", 5.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -30, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Buy infiltration gear.">Buy infiltration gear. (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 30}])>>)</span><br>
<</if>>
<<link "Walk the pens." "Canvas_underworld_market_shop_Node_pens">><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Leave the stalls." "Location_underworld_market">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She buys a blade the way a soldier does — by balance, not by looks — and a thing to hide it in. Her hands are deadlier leaving than they were coming in. Coin well spent.</p>
<<set $game_state.current_canvas = "underworld_market_shop">>
<<set $game_state.current_node = "underworld_market_shop.weapon">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_market_shop.weapon") === -1) { vn.push("underworld_market_shop.weapon"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Stow it.->Location_underworld_market]]<p>Soft soles, a light that isn't a light, a tool roll that opens what's meant to stay shut. The kind of kit that turns a careful woman into one nobody sees. Coin well spent.</p>
<<set $game_state.current_canvas = "underworld_market_shop">>
<<set $game_state.current_node = "underworld_market_shop.gear">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_market_shop.gear") === -1) { vn.push("underworld_market_shop.gear"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Pack it away.->Location_underworld_market]]<p>Behind the chain they keep people the way the market keeps anything — by weight and condition, tagged, waiting on a buyer. She walks the line because she makes herself walk it. Most are just people, stolen and sold. But one of them, a girl too still, too even, watches her back with eyes that don't track quite right — and something in Wren's chest does a thing it has no name for, a recognition she shuts off before it finishes. She's looked at her own reflection in a cage. She shuts it down and walks out.</p>
<<set $game_state.current_canvas = "underworld_market_shop">>
<<set $game_state.current_node = "underworld_market_shop.pens">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_market_shop.pens") === -1) { vn.push("underworld_market_shop.pens"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Don't look again.->Location_underworld_market]]<p>The ring is a pit of packed dirt under a single bulb, the crowd three deep around it laying coin on the next body to fall. The matchmaker looks her up and down, unimpressed, and asks what she thinks she can take. Down here her fists pay the same as the rest of her.</p>
<<set $game_state.current_canvas = "underworld_pit_fight">>
<<set $game_state.current_node = "underworld_pit_fight.base">>
<<script>>setup.markCanvasTriggered("underworld_pit_fight");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_pit_fight.base") === -1) { vn.push("underworld_pit_fight.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 10}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('underworld_pit_fight:cc0') ? '' : 'unlocked-choice'">
<<link "Take a low bout." "Canvas_underworld_pit_fight_Node_win_low">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 8.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_pit_fight:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Take a low bout.">Take a low bout. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 30}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 20}])>>
<span @class="setup.isChoiceVisited('underworld_pit_fight:cc1') ? '' : 'unlocked-choice'">
<<link "Take a hard bout." "Canvas_underworld_pit_fight_Node_win_hard">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", 20.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -20, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_pit_fight:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Take a hard bout.">Take a hard bout. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 20}])>>)</span><br>
<</if>>
<</if>>
<<link "Just watch tonight." "Location_underworld_pit">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>They put a brawler in with her, slow and heavy. She lets him swing, reads the half-beat, and ends it with the kind of economy the crowd doesn't cheer because it scared them. The matchmaker pays out without meeting her eyes.</p>
<<set $game_state.current_canvas = "underworld_pit_fight">>
<<set $game_state.current_node = "underworld_pit_fight.win_low">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_pit_fight.win_low") === -1) { vn.push("underworld_pit_fight.win_low"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Take the purse.->Location_underworld_pit]]<p>This one's real — fast, trained, in it to hurt her. For a few seconds it's almost a fight. Then the thing they built her to be takes over, and what's left on the dirt isn't getting up. The crowd goes quiet. The purse is heavy. Someone in the dark is watching her now.</p>
<<set $game_state.current_canvas = "underworld_pit_fight">>
<<set $game_state.current_node = "underworld_pit_fight.win_hard">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_pit_fight.win_hard") === -1) { vn.push("underworld_pit_fight.win_hard"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Take the purse.->Location_underworld_pit]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 0}]})>>
<p>He holds her head still and finishes in her mouth; she takes it, swallows because the trade expects it, and sits back on her heels. He's already reaching for his drink.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_finish_facial_t5"] to ($game_state.media_cycle["sex_brothel_finish_facial_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_finish_facial_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_finish_facial_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_finish_facial_t5/c6d8642c4ce.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_finish_facial_t5/c357c6f5e3b.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_finish_facial_t5/c11885f84f7.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_finish_facial_t5/cf44f170f7a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 1}]})>>
<p>He finishes inside her with a grunt, holds, then pulls out. His cum slides down the inside of her thigh. She waits the polite beat and lifts off, and counts the coin into her palm.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_finish_inside_t5"] to ($game_state.media_cycle["sex_brothel_finish_inside_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_finish_inside_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_finish_inside_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_finish_inside_t5/c337e9650c8.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_finish_inside_t5/cb957733146.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_finish_inside_t5/cc9de118c53.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_finish_inside_t5/cf82c9b4a8d.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "sex_finisher_type", "operator": "eq", "value": 2}]})>>
<p>He finishes in her ass and stays a moment, breathing, then steps back. His weight and his cum and then nothing — the same nothing as every man down here. She straightens. The next one's already on the stairs.</p><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_finish_ass_t5"] to ($game_state.media_cycle["sex_brothel_finish_ass_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_finish_ass_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_finish_ass_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_finish_ass_t5/c0e3bde7dad.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_finish_ass_t5/c4ce7281131.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_finish_ass_t5/c24ff40fdee.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_finish_ass_t5/cfc8b2c7e60.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>>
<</if>>
<<set $game_state.current_canvas = "underworld_brothel_finisher">>
<<set $game_state.current_node = "underworld_brothel_finisher.climax">>
<<script>>setup.markCanvasTriggered("underworld_brothel_finisher");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_finisher.climax") === -1) { vn.push("underworld_brothel_finisher.climax"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "coin", "add", 30.0, false, null);setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);setup.showEffectNotification();<</script>>
[[Clean up. Collect.->Location_underworld_brothel]]<p>The madam points her at a door. The man inside doesn't look up from his drink until she's undressed — then he looks her over the way men look over rented things, deciding where to start. No name, no preamble. He paid for the hour and means to use it.</p>
<<set $game_state.current_canvas = "underworld_brothel_loop">>
<<set $game_state.current_node = "underworld_brothel_loop.intro">>
<<script>>setup.markCanvasTriggered("underworld_brothel_loop");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_loop.intro") === -1) { vn.push("underworld_brothel_loop.intro"); }<</script>>
<<nobr>>
<<link "Down on your knees." "Canvas_underworld_brothel_loop_Node_base_oral">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_oral_t5"] to ($game_state.media_cycle["sex_brothel_oral_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_oral_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_oral_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_oral_t5/c149abc39d7.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_oral_t5/ca8e8acc6b0.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_oral_t5/cd99a4b7363.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_oral_t5/cfd198e1bff.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>On her knees between his, she takes his cock in her mouth and works it the way the trade taught her — steady, practiced, her eyes somewhere past his hip. He fists a hand in her hair and sets the pace. She lets him. None of it reaches her.</p>
<<set $game_state.current_canvas = "underworld_brothel_loop">>
<<set $game_state.current_node = "underworld_brothel_loop.base_oral">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_loop.base_oral") === -1) { vn.push("underworld_brothel_loop.base_oral"); }<</script>>
<<nobr>>
<<link "Keep sucking him." "Canvas_underworld_brothel_loop_Node_base_oral">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Climb on and ride him." "Canvas_underworld_brothel_loop_Node_base_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Turn around and give him your ass." "Canvas_underworld_brothel_loop_Node_base_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('underworld_brothel_loop:cc0') ? '' : 'unlocked-choice'">
<<link "Let him finish in your mouth." "Canvas_underworld_brothel_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_brothel_loop:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_vaginal_t5"] to ($game_state.media_cycle["sex_brothel_vaginal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_vaginal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_vaginal_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_vaginal_t5/c450060d15a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_vaginal_t5/cdb6120d3c5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_vaginal_t5/ceac1aa782c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_vaginal_t5/cf64e33c40c.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She climbs on and takes his cock without ceremony, sinks down and rides, hips keeping a rhythm that has nothing to do with her. He paws at her tits and fucks up into her; she gives him the small sounds the trade expects and feels none of them.</p>
<<set $game_state.current_canvas = "underworld_brothel_loop">>
<<set $game_state.current_node = "underworld_brothel_loop.base_vaginal">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_loop.base_vaginal") === -1) { vn.push("underworld_brothel_loop.base_vaginal"); }<</script>>
<<nobr>>
<<link "Keep riding him." "Canvas_underworld_brothel_loop_Node_base_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back down on your knees." "Canvas_underworld_brothel_loop_Node_base_oral">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Let him have your ass." "Canvas_underworld_brothel_loop_Node_base_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 2.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}]})>>
<span @class="setup.isChoiceVisited('underworld_brothel_loop:cc1') ? '' : 'unlocked-choice'">
<<link "Let him finish inside you." "Canvas_underworld_brothel_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_brothel_loop:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_brothel_anal_t5"] to ($game_state.media_cycle["sex_brothel_anal_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_brothel_anal_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_brothel_anal_t5"]>><<if _mc is 0>><video src="./videos/sex/brothel_anal_t5/c0e943a0442.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/brothel_anal_t5/c7e7b540eff.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/brothel_anal_t5/cd22d1b3c41.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/brothel_anal_t5/ce556b0edee.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>Bent over the edge of the bed, she takes him in the ass in long flat strokes, both his hands clamped on her hips. Just more friction down a body that gives nothing back. She braces and waits it out, counting somewhere behind her own face what the hour's worth.</p>
<<set $game_state.current_canvas = "underworld_brothel_loop">>
<<set $game_state.current_node = "underworld_brothel_loop.base_anal">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_loop.base_anal") === -1) { vn.push("underworld_brothel_loop.base_anal"); }<</script>>
<<nobr>>
<<link "Take it." "Canvas_underworld_brothel_loop_Node_base_anal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "add", (Math.floor(Math.random() * 7) + 8), false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<link "Back onto his cock." "Canvas_underworld_brothel_loop_Node_base_vaginal">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "loop_npc_pleasure", "operator": "gte", "value": 50}, {"type": "trait", "subject": "player", "trait_key": "anal_active", "operator": "gte", "value": 1}]})>>
<span @class="setup.isChoiceVisited('underworld_brothel_loop:cc2') ? '' : 'unlocked-choice'">
<<link "Let him finish in your ass." "Canvas_underworld_brothel_finisher_Node_climax">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_brothel_loop:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<</nobr>><p>The madam counts her with one look and tips her head at the rooms. No contract, no company, no Mercer signing her out — just a door, a client, and coin at the end that goes in her pocket and nowhere else. The thing she's built for, sold for once, on her own account.</p>
<<set $game_state.current_canvas = "underworld_brothel_work">>
<<set $game_state.current_node = "underworld_brothel_work.base">>
<<script>>setup.markCanvasTriggered("underworld_brothel_work");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_brothel_work.base") === -1) { vn.push("underworld_brothel_work.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<<link "Take a client." "Canvas_underworld_brothel_loop_Node_intro">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "loop_npc_pleasure", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_stage", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_finisher_type", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "anal_active", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "sex_entry_origin", "set", 0.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Take a client.">Take a client. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<<link "Not tonight." "Location_underworld_brothel">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><img src="./videos/locations/underworld_strip.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She walks back up the strip — the house, the pit, the market closing behind her, and past them the dark where the real city keeps its teeth. Down here nobody was looking for her. It's the closest thing to free she's felt.</p>
<<set $game_state.current_canvas = "underworld_strip_hub">>
<<set $game_state.current_node = "underworld_strip_hub.base">>
<<script>>setup.markCanvasTriggered("underworld_strip_hub");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_strip_hub.base") === -1) { vn.push("underworld_strip_hub.base"); }<</script>>
<<script>>advanceTime(30);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", -10.0, true, null);setup.showEffectNotification();<</script>>
[[Out to the gate.->Location_underworld_gate]]<img src="./videos/locations/underworld_gate.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The man at the door doesn't ask her name. He looks her over the way a turnstile would and holds out a hand. "Coin," he says. "Or you're not my problem." Behind him the stairs go down into red light and noise. There's more than one way through a man like this. The coin he wants isn't company credits — it's underworld scrip: earned down here, spent down here, worth nothing up top.</p>
<<set $game_state.current_canvas = "underworld_gate_check">>
<<set $game_state.current_node = "underworld_gate_check.base">>
<<script>>setup.markCanvasTriggered("underworld_gate_check");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_gate_check.base") === -1) { vn.push("underworld_gate_check.base"); }<</script>>
<<nobr>>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 5}])>>
<<link "Pay the toll." "Location_underworld_strip">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -5, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="Pay the toll.">Pay the toll. (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 5}])>>)</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 15}]})>>
<span @class="setup.isChoiceVisited('underworld_gate_check:cc0') ? '' : 'unlocked-choice'">
<<link "Force past him." "Location_underworld_strip">><<script>>setup.markChoiceVisited("underworld_gate_check:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})>>
<<if setup.checkCostsAffordable([{"trait": "arousal_charge", "value": 1}])>>
<span @class="setup.isChoiceVisited('underworld_gate_check:cc1') ? '' : 'unlocked-choice'">
<<link "Put the weapon on him." "Canvas_underworld_gate_check_Node_weapon_scene">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "add", -1, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("underworld_gate_check:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Put the weapon on him.">Put the weapon on him. (<<= setup.getCostBlockedMessage([{"trait": "arousal_charge", "value": 1}])>>)</span><br>
<</if>>
<</if>>
<<link "Turn back." "Location_the_waterfront">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_arousal_weapon_use_t5"] to ($game_state.media_cycle["sex_arousal_weapon_use_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_arousal_weapon_use_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_arousal_weapon_use_t5"]>><<if _mc is 0>><video src="./videos/sex/arousal_weapon_use_t5/c80a1baf7f5.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/arousal_weapon_use_t5/c759429da3f.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/arousal_weapon_use_t5/c44022617b2.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/arousal_weapon_use_t5/cf66883e910.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She thumbs the emitter and the doorman's whole face changes — the bored menace gone, breath short, the hand that wanted coin wanting something else now. She walks him back into the shadow of the stair, gets his cock out, and rides him against the wall in quick flat strokes, fast and clinical, giving him nothing but the friction. He spills into her with his eyes rolling white and sags down the bricks, under. He'll wake on the steps in an hour with no memory, and never know she walked past him. The stairs are open.</p>
<<set $game_state.current_canvas = "underworld_gate_check">>
<<set $game_state.current_node = "underworld_gate_check.weapon_scene">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("underworld_gate_check.weapon_scene") === -1) { vn.push("underworld_gate_check.weapon_scene"); }<</script>>
<<script>>advanceTime(60);<</script>>
[[Down into the strip.->Location_underworld_strip]]<img src="./videos/locations/kess_berth.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}]})>>
<p>The emitter lies on Kess's bench between two things he is not working on. One core, one weapon — it feeds the drain inside her or the thing in her hand, never both at once. Right now it's the drain.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<p>The emitter's live in her coat, the drain inside her gone quiet behind it. One core, one weapon. Right now it's the emitter, and the gate is the only place down here that cares.</p>
<</if>>
<<set $game_state.current_canvas = "activity_swap_weapon_berth">>
<<set $game_state.current_node = "activity_swap_weapon_berth.base">>
<<script>>setup.markCanvasTriggered("activity_swap_weapon_berth");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_swap_weapon_berth.base") === -1) { vn.push("activity_swap_weapon_berth.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}]})>>
<span @class="setup.isChoiceVisited('activity_swap_weapon_berth:cc0') ? '' : 'unlocked-choice'">
<<link "Run the emitter instead." "Location_kess_berth">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_swap_weapon_berth:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<span @class="setup.isChoiceVisited('activity_swap_weapon_berth:cc1') ? '' : 'unlocked-choice'">
<<link "Go back to the drain." "Location_kess_berth">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_swap_weapon_berth:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave it." "Location_the_cot">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}]})>>
<p>The emitter sits charged on the bench. One core, one weapon — it feeds the drain inside her or the thing in her hand, never both at once. Right now it's the drain.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<p>The emitter's live in her hand, the drain inside her gone quiet behind it. One core, one weapon. Right now it's the emitter.</p>
<</if>>
<<set $game_state.current_canvas = "activity_swap_weapon">>
<<set $game_state.current_node = "activity_swap_weapon.base">>
<<script>>setup.markCanvasTriggered("activity_swap_weapon");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_swap_weapon.base") === -1) { vn.push("activity_swap_weapon.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 1}]})>>
<span @class="setup.isChoiceVisited('activity_swap_weapon:cc0') ? '' : 'unlocked-choice'">
<<link "Run the emitter instead." "Location_wren_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 2.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_swap_weapon:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}]})>>
<span @class="setup.isChoiceVisited('activity_swap_weapon:cc1') ? '' : 'unlocked-choice'">
<<link "Go back to the drain." "Location_wren_room">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "equipped_weapon", "set", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_swap_weapon:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<link "Leave it." "Location_wren_room">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The emitter sits on her bench, casing off, guts exposed. She knows the architecture of it the way she knows her own — because it is her own, scaled down to a handheld. Heat-warp on the field coils, a cracked driver. Slow work, and hers alone.</p>
<<set $game_state.current_canvas = "activity_repair_weapon">>
<<set $game_state.current_node = "activity_repair_weapon.base">>
<<script>>setup.markCanvasTriggered("activity_repair_weapon");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_repair_weapon.base") === -1) { vn.push("activity_repair_weapon.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "weapon_repair", "operator": "lt", "value": 2}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_repair_weapon:cc0') ? '' : 'unlocked-choice'">
<<link "Work on it." "Canvas_activity_repair_weapon_Node_progress">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "weapon_repair", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_repair_weapon:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work on it.">Work on it. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "weapon_repair", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "weapon_repair", "operator": "lt", "value": 3}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_repair_weapon:cc1') ? '' : 'unlocked-choice'">
<<link "Finish it." "Canvas_activity_repair_weapon_Node_done">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "weapon_repair", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "set", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "arousal_weapon_ready", "set");<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_repair_weapon:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Finish it.">Finish it. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<link "Leave it for now." "Location_wren_room">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "weapon_repair", "operator": "lt", "value": 2}]})>>
<p>She straightens the field coils, one pass, and tests the draw. Dead still — but cleaner dead than it was. More to do.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "weapon_repair", "operator": "gte", "value": 2}]})>>
<p>Driver reseated, coils true. It draws a charge now and holds it — a flicker of the field crossing her own skin, the faint unwanted warmth of it. Almost. One more pass and it fires.</p>
<</if>>
<<set $game_state.current_canvas = "activity_repair_weapon">>
<<set $game_state.current_node = "activity_repair_weapon.progress">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_repair_weapon.progress") === -1) { vn.push("activity_repair_weapon.progress"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Set it down.->Location_wren_room]]<p>Last pass. She powers it and the field comes up clean, a low hum she feels in her teeth — and, testing it on herself a half-second too long, a heat low in her belly she didn't ask for and shuts off fast. It works. A second weapon now, hers, that the company never issued and doesn't know she has. A way past any man she can't fight.</p>
<<set $game_state.current_canvas = "activity_repair_weapon">>
<<set $game_state.current_node = "activity_repair_weapon.done">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_repair_weapon.done") === -1) { vn.push("activity_repair_weapon.done"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Stow it.->Location_wren_room]]<p>The strongroom is what he saved. Two personnel files, a man each — his own people, the two who moved the materials. Both stamped CLOSED. Under each, a death notice, and under that, more notices: wives, children, the whole line, the dates all the same night. Renner didn't just fire them. He erased them. And paperclipped to the back, the thing he's really hiding: where the two men came from before Vance — the same name twice, a place down in the city that isn't on any map. The underworld. That's the thread Cain followed back. That's where it goes.</p><p>Standing in it, something turns over in her — not a thought, a feeling, brief and uninvited: she has stood in a room like this before, on the other side of the file. Then it's gone, filed under nothing.</p>
<<set $game_state.current_canvas = "yard_find_3">>
<<set $game_state.current_node = "yard_find_3.base">>
<<script>>setup.markCanvasTriggered("yard_find_3");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_find_3.base") === -1) { vn.push("yard_find_3.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "found_dead_men", "set");<</script>>
[[Take what matters. Go.->Location_renner_burned_yard]]<img src="./videos/activities/arousal_weapon.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>In a melted equipment locker, half-slagged but whole at the core, is a device she knows in her hands before she knows in her head — the same build as the thing inside her, the one that makes a body want against its own sense. A field emitter. Renner had one off the line and never fired it. Heat-warped, dead. But the core is sound. She pockets it. Fixed, it's a way past any man who'd otherwise put her down.</p>
<<set $game_state.current_canvas = "yard_find_2">>
<<set $game_state.current_node = "yard_find_2.base">>
<<script>>setup.markCanvasTriggered("yard_find_2");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_find_2.base") === -1) { vn.push("yard_find_2.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "has_arousal_weapon", "set");<</script>>
[[Take it. Get out clean.->Location_renner_burned_yard]]<p>Inside the outer line she finds the shape of it under the burn: not just a storage yard. Loading bays built for things heavier than crates, restraint points set into a slab, a billing wall scorched but legible — shipments logged out to the same Vance facility she's already walked the ashes of. Renner moved more than equipment through here. He just never asked what for.</p>
<<set $game_state.current_canvas = "yard_find_1">>
<<set $game_state.current_node = "yard_find_1.base">>
<<script>>setup.markCanvasTriggered("yard_find_1");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_find_1.base") === -1) { vn.push("yard_find_1.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "yard_clue_seen", "set");<</script>>
[[Log it. Keep moving.->Location_renner_burned_yard]]<img src="./videos/locations/renner_burned_yard.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 1}]})>>
<p>The fence is down on the water side. Past it the yard is black glass and bent steel — and a guard with a torch, walking the line where the gate used to be. Getting in means getting past him.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 3}]})>>
<p>She's inside the outer line now. Deeper in there are more lights and more men — Renner doesn't watch a dead yard this hard for nothing. Whatever's worth guarding is further back.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 3}]})>>
<p>She's been all the way to the back of it. There's nothing left in the yard she hasn't already put her hands on.</p>
<</if>>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.base">>
<<script>>setup.markCanvasTriggered("yard_crawl");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.base") === -1) { vn.push("yard_crawl.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 1}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('yard_crawl:cc0') ? '' : 'unlocked-choice'">
<<link "Push in past the gate." "Canvas_yard_crawl_Node_guard1">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Push in past the gate.">Push in past the gate. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 2}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('yard_crawl:cc1') ? '' : 'unlocked-choice'">
<<link "Work deeper in." "Canvas_yard_crawl_Node_guard2">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Work deeper in.">Work deeper in. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 3}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('yard_crawl:cc2') ? '' : 'unlocked-choice'">
<<link "Push to the back of it." "Canvas_yard_crawl_Node_guard3">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Push to the back of it.">Push to the back of it. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<link "Slip back out." "Location_the_waterfront">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>One man, one torch, bored and slow — the easy one, the test before the real ones. He hasn't seen her yet.</p>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.guard1">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.guard1") === -1) { vn.push("yard_crawl.guard1"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 10}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc3') ? '' : 'unlocked-choice'">
<<link "Slip past him, unseen." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 10}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc4') ? '' : 'unlocked-choice'">
<<link "Take him down." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc4");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc5') ? '' : 'unlocked-choice'">
<<link "Use the weapon on him." "Canvas_yard_crawl_Node_weapon_scene">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "add", -1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc5");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc6') ? '' : 'unlocked-choice'">
<<link "He's on you — break and run." "Canvas_yard_crawl_Node_flee">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc6");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 10}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 10}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}]})))>>
<<script>>
try {
window._engineNoExitsDiag_yard_crawl = [{"text": "Slip past him, unseen.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 10}]}}, {"text": "Take him down.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 10}]}}, {"text": "Use the weapon on him.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]}}, {"text": "He's on you \u2014 break and run.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 10}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "yard_crawl",
choices: window._engineNoExitsDiag_yard_crawl,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_yard_crawl>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas yard_crawl</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_renner_burned_yard]]
<</if>>
<</nobr>><p>Two of them here, awake and armed, working a pattern between the racks. Past the easy line — these ones would hurt her if she let them.</p>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.guard2">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.guard2") === -1) { vn.push("yard_crawl.guard2"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 25}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc7') ? '' : 'unlocked-choice'">
<<link "Slip past them, unseen." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc7");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 25}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc8') ? '' : 'unlocked-choice'">
<<link "Take them down." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc8");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc9') ? '' : 'unlocked-choice'">
<<link "Use the weapon on them." "Canvas_yard_crawl_Node_weapon_scene">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "add", -1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc9");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc10') ? '' : 'unlocked-choice'">
<<link "Too many — break and run." "Canvas_yard_crawl_Node_flee">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc10");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 25}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 25}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}]})))>>
<<script>>
try {
window._engineNoExitsDiag_yard_crawl = [{"text": "Slip past them, unseen.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 25}]}}, {"text": "Take them down.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 25}]}}, {"text": "Use the weapon on them.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]}}, {"text": "Too many \u2014 break and run.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "yard_crawl",
choices: window._engineNoExitsDiag_yard_crawl,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_yard_crawl>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas yard_crawl</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_renner_burned_yard]]
<</if>>
<</nobr>><p>The heaviest watch is here, at the back — a knot of his best men around a strongroom the fire didn't reach. Whatever Renner's keeping, it's behind them.</p>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.guard3">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.guard3") === -1) { vn.push("yard_crawl.guard3"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 40}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc11') ? '' : 'unlocked-choice'">
<<link "Ghost straight through them." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc11");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 40}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc12') ? '' : 'unlocked-choice'">
<<link "Cut through them." "Canvas_yard_crawl_Node_deeper">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc12");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc13') ? '' : 'unlocked-choice'">
<<link "Use the weapon on them." "Canvas_yard_crawl_Node_weapon_scene">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "yard_depth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "add", -1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "hygiene", "add", -30.0, true, null);<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc13");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}]})>>
<span @class="setup.isChoiceVisited('yard_crawl:cc14') ? '' : 'unlocked-choice'">
<<link "Not yet — break and run." "Canvas_yard_crawl_Node_flee">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyFlag("player", null, "yard_caught_once", "set");<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("yard_crawl:cc14");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 40}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 40}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}]})))>>
<<script>>
try {
window._engineNoExitsDiag_yard_crawl = [{"text": "Ghost straight through them.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 40}]}}, {"text": "Cut through them.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 40}]}}, {"text": "Use the weapon on them.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}, {"type": "trait", "subject": "player", "trait_key": "equipped_weapon", "operator": "eq", "value": 2}, {"type": "flag", "subject": "player", "flag_key": "arousal_weapon_ready", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "arousal_charge", "operator": "gte", "value": 1}, {"type": "trait", "subject": "player", "trait_key": "emitter_broken", "operator": "lt", "value": 1}]}}, {"text": "Not yet \u2014 break and run.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 40}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "yard_crawl",
choices: window._engineNoExitsDiag_yard_crawl,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_yard_crawl>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas yard_crawl</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_renner_burned_yard]]
<</if>>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 2}]})>>
<p>She's through the gate and into the outer yard, the guard behind her none the wiser or none the able. The dark swallows her.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 2}, {"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "lt", "value": 3}]})>>
<p>Deeper now, in among the racks where the lights are — and past the men who'd have stopped her. She keeps to the shadow and goes on.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "yard_depth", "operator": "gte", "value": 3}]})>>
<p>The back of the yard, the strongroom, the thing they were all standing around. She's in.</p>
<</if>>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.deeper">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.deeper") === -1) { vn.push("yard_crawl.deeper"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[See what's here.->Location_renner_burned_yard]]<<if ndef $game_state.media_cycle>><<set $game_state.media_cycle to {}>><</if>><<set $game_state.media_cycle["sex_yard_emitter_fire_t5"] to ($game_state.media_cycle["sex_yard_emitter_fire_t5"] === undefined ? 0 : ($game_state.media_cycle["sex_yard_emitter_fire_t5"] + 1) % 4)>><<set _mc to $game_state.media_cycle["sex_yard_emitter_fire_t5"]>><<if _mc is 0>><video src="./videos/sex/yard_emitter_fire_t5/c0aafd22138.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 1>><video src="./videos/sex/yard_emitter_fire_t5/c2e25cd412a.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<elseif _mc is 2>><video src="./videos/sex/yard_emitter_fire_t5/c53ec986e91.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><<else>><video src="./videos/sex/yard_emitter_fire_t5/incumbent.mp4" autoplay muted loop playsinline controls preload="metadata" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;"></video><</if>><p>She thumbs the emitter and the field rolls out ahead of her. Whoever's in reach goes stupid with it inside a breath — pupils blown, breath ragged, the gun forgotten in a slack hand. She picks the nearest, drags his cock out of his fatigues, and sinks down onto it against the crates, riding him in hard flat strokes while the others sway on their feet. There's nothing in it on her side — just leverage; she works him until he spills into her and drops boneless and out. The rest fold a beat later. By the time any of them surface she's gone, deeper in, his cum drying on her thigh and nothing in her face at all.</p>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.weapon_scene">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.weapon_scene") === -1) { vn.push("yard_crawl.weapon_scene"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Step over them. On.->Location_renner_burned_yard]]<p>It goes against her — too many, or too sharp, or her own body too slow. She breaks for the downed fence and is over the water side before the torches swing round. Nothing gained tonight. She walks it off.</p>
<<set $game_state.current_canvas = "yard_crawl">>
<<set $game_state.current_node = "yard_crawl.flee">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("yard_crawl.flee") === -1) { vn.push("yard_crawl.flee"); }<</script>>
<<script>>advanceTime(60);<</script>>
[[Get clear.->Location_the_waterfront]]<img src="./videos/locations/facility_ruins.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The facility is a black skeleton against the dock lights — scorched frame, melted racks, the smell of old smoke that never washed out. She walks it slowly, cataloguing: where the containment was, where the fire started, what the dead machines used to do. None of it matches the briefing. All of it was built to hold things that could feel — and someone came through and let them out before he burned it down. The dread-place. The first one.</p>
<<set $game_state.current_canvas = "inv_facility_explore">>
<<set $game_state.current_node = "inv_facility_explore.base">>
<<script>>setup.markCanvasTriggered("inv_facility_explore");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("inv_facility_explore.base") === -1) { vn.push("inv_facility_explore.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Done here.->Location_facility_ruins]]<p>The floor's empty, the door shut. No one watches an asset in its own quarters. She uses the hour the way the company never told her to — drilling the body they built, sharpening it past the spec on her file. It's the one thing here that's only hers.</p>
<<set $game_state.current_canvas = "activity_train">>
<<set $game_state.current_node = "activity_train.base">>
<<script>>setup.markCanvasTriggered("activity_train");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_train.base") === -1) { vn.push("activity_train.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "lt", "value": 30}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_train:cc0') ? '' : 'unlocked-choice'">
<<link "Combat drill." "Canvas_activity_train_Node_combat">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "fighting", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_train:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Combat drill.">Combat drill. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 30}, {"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "lt", "value": 50}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_train:cc1') ? '' : 'unlocked-choice'">
<<link "Combat drill." "Canvas_activity_train_Node_combat">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "fighting", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_train:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Combat drill.">Combat drill. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "yard_caught_once", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 30}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_train:cc2') ? '' : 'unlocked-choice'">
<<link "Stealth drill." "Canvas_activity_train_Node_stealth">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "stealth", "add", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_train:cc2");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Stealth drill.">Stealth drill. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "yard_caught_once", "operator": "is_true"}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 30}, {"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 50}]})>>
<<if setup.checkCostsAffordable([{"trait": "energy", "value": 15}])>>
<span @class="setup.isChoiceVisited('activity_train:cc3') ? '' : 'unlocked-choice'">
<<link "Stealth drill." "Canvas_activity_train_Node_stealth">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "stealth", "add", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", -15, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>setup.markChoiceVisited("activity_train:cc3");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Stealth drill.">Stealth drill. (<<= setup.getCostBlockedMessage([{"trait": "energy", "value": 15}])>>)</span><br>
<</if>>
<</if>>
<<link "Enough for now." "Location_wren_room">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "lt", "value": 25}]})>>
<p>She runs the forms against the empty air — strikes, breaks, the throw that ends a man. Her body obeys a half-beat slow, the way a tool obeys before it's been worked hard. Clumsy still. But the half-beat is shorter than it was yesterday.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "fighting", "operator": "gte", "value": 25}]})>>
<p>No hesitation now. The forms come fast and flat and final — strike, break, the throw — each one landing where a throat or a knee would be. Whatever they built her for, this wasn't on the spec. She's better than the file says. She keeps that to herself.</p>
<</if>>
<<set $game_state.current_canvas = "activity_train">>
<<set $game_state.current_node = "activity_train.combat">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_train.combat") === -1) { vn.push("activity_train.combat"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Rack it. Catch your breath.->Location_wren_room]]<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "lt", "value": 25}]})>>
<p>She practices the small things — weight rolled onto the outside of the foot, breath dropped low, the timing of a turned back. Her own joints betray her with a tick of sound she'd never have caught a week ago. She catches it now. That's the start of it.</p>
<<elseif setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "stealth", "operator": "gte", "value": 25}]})>>
<p>She moves through her own quarters and the room doesn't register her — no sound, no shift of air, nothing for a camera to hold. A thing that can't be seen can go where it isn't allowed. She doesn't let the thought finish.</p>
<</if>>
<<set $game_state.current_canvas = "activity_train">>
<<set $game_state.current_node = "activity_train.stealth">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_train.stealth") === -1) { vn.push("activity_train.stealth"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Ease off. Settle.->Location_wren_room]]<img src="./videos/activities/chip_view.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She opens the store the company gave her for what it calls recovered assets — the place a restored memory would go, if she ever recovered one. It's empty. It has always been empty.</p><p>There is one slot in it, greyed out, labelled in a hand that isn't the company's: FRAGMENT 01 — LOCKED. She doesn't remember making the label. She closes the store — and notices she's opened it more often than the task log would ever justify. She closes that thought too.</p>
<<set $game_state.current_canvas = "canvas_chip_view">>
<<set $game_state.current_node = "canvas_chip_view.base">>
<<script>>setup.markCanvasTriggered("canvas_chip_view");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_chip_view.base") === -1) { vn.push("canvas_chip_view.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
[[Close it.->Location_cradle]]<p>The pad, the tap spliced off Kess's feed line, the bag underneath and the coat on the nail. Not the cradle. But it holds a charge, and down here that's the whole of what she needs a bed to do.</p>
<<set $game_state.current_canvas = "activity_kess_cot">>
<<set $game_state.current_node = "activity_kess_cot.base">>
<<script>>setup.markCanvasTriggered("activity_kess_cot");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_kess_cot.base") === -1) { vn.push("activity_kess_cot.base"); }<</script>>
<<nobr>>
<<link "Crash rough — a couple hours, no coin. (Half a charge.)" "Canvas_activity_kess_cot_Node_rough">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 40.0, true, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<if setup.checkCostsAffordable([{"trait": "coin", "value": 10}])>>
<<link "A night on the feed line. (10 coin — full charge, weapons reloaded.)" "Canvas_activity_kess_cot_Node_night">><<script>>setup.pendingEffects = [];<</script>><<script>>setup.applyAndNotifyTrait("player", null, "energy", "add", 100.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 1.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "arousal_charge", "set", 3.0, false, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "feed_line_days", "set", 3.0, true, null);<</script>><<script>>setup.applyAndNotifyTrait("player", null, "coin", "add", -10, false, null);<</script>><<script>>setup.showEffectNotification();<</script>><<script>>advanceTime(3);<</script>><</link>><br>
<<else>>
<span class="locked-choice" title="A night on the feed line. (10 coin — full charge, weapons reloaded.)">A night on the feed line. (10 coin — full charge, weapons reloaded.) (<<= setup.getCostBlockedMessage([{"trait": "coin", "value": 10}])>>)</span><br>
<</if>>
<<link "Leave it." "Location_the_cot">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>She doesn't pay for the line — just drops onto the pad and lets the cot's trickle-tap do what it can. A couple hours of half-sleep, one eye on the water, and her cells claw back partway. Not full. Enough to move. She's up before she's rested, the way the Reach teaches you to be.</p>
<<set $game_state.current_canvas = "activity_kess_cot">>
<<set $game_state.current_node = "activity_kess_cot.rough">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_kess_cot.rough") === -1) { vn.push("activity_kess_cot.rough"); }<</script>>
<<script>>advanceTime(120);<</script>>
[[Up.->Location_the_cot]]<p>She pays Kess his coin and hooks into the feed line proper. It's not the cradle's clean pull — it's a breaking-dock rig meant for hulls — but it takes her down the whole way and holds her there till morning. Charge full, the drain rebuilt its one shot off the same current, and she bleeds the tail of it into the emitter's cell until that reads three. The night walked back into her. She unhooks into a new day.</p>
<<set $game_state.current_canvas = "activity_kess_cot">>
<<set $game_state.current_node = "activity_kess_cot.night">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_kess_cot.night") === -1) { vn.push("activity_kess_cot.night"); }<</script>>
<<script>>advanceTime(540);<</script>>
[[Morning.->Location_the_cot]]<p>She bleeds the cradle's charge into the emitter's cell until it reads full — three. Then she pockets it and goes.</p>
<<set $game_state.current_canvas = "activity_recharge_emitter">>
<<set $game_state.current_node = "activity_recharge_emitter.base">>
<<script>>setup.markCanvasTriggered("activity_recharge_emitter");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_recharge_emitter.base") === -1) { vn.push("activity_recharge_emitter.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "arousal_charge", "set", 3.0, false, null);setup.showEffectNotification();<</script>>
[[Done.->Location_cradle]]<p>A few minutes on the feed line and the drain rebuilds its one shot. Loaded. She unhooks.</p>
<<set $game_state.current_canvas = "activity_recharge_drain">>
<<set $game_state.current_node = "activity_recharge_drain.base">>
<<script>>setup.markCanvasTriggered("activity_recharge_drain");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_recharge_drain.base") === -1) { vn.push("activity_recharge_drain.base"); }<</script>>
<<script>>advanceTime(60);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "drain_charge", "set", 1.0, false, null);setup.showEffectNotification();<</script>>
[[Done.->Location_cradle]]<p>Not a full night — just a couple hours hooked to the cradle's feed line, eyes open. Her cells pull back to full. Then she unhooks, the day still going.</p>
<<set $game_state.current_canvas = "activity_charge_up">>
<<set $game_state.current_node = "activity_charge_up.base">>
<<script>>setup.markCanvasTriggered("activity_charge_up");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_charge_up.base") === -1) { vn.push("activity_charge_up.base"); }<</script>>
<<script>>advanceTime(120);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", 100.0, true, null);setup.showEffectNotification();<</script>>
[[Unhook. Back to it.->Location_cradle]]<p>She settles back into the cradle. The contacts find their points along her spine, cold and familiar. The room dims as the charge takes her down, and the quiet closes over the top of her like water. When it lets her go it's morning again — a new day, the charge full, the hours she spent out there walked back into her.</p>
<<set $game_state.current_canvas = "activity_recharge">>
<<set $game_state.current_node = "activity_recharge.base">>
<<script>>setup.markCanvasTriggered("activity_recharge");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_recharge.base") === -1) { vn.push("activity_recharge.base"); }<</script>>
<<script>>advanceTime(540);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", 100.0, true, null);setup.showEffectNotification();<</script>>
[[Power up.->Location_cradle]]<p>She gives the address of a door that isn't on any manifest and waits. The car comes down for her the way it came down before — no questions, no record of the fare. Up to the towers, or only as far as the docks.</p>
<<set $game_state.current_canvas = "activity_travel_from_gate">>
<<set $game_state.current_node = "activity_travel_from_gate.base">>
<<script>>setup.markCanvasTriggered("activity_travel_from_gate");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_from_gate.base") === -1) { vn.push("activity_travel_from_gate.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('activity_travel_from_gate:cc0') ? '' : 'unlocked-choice'">
<<link "Ride up to the Spire." "Canvas_activity_travel_from_gate_Node_riding_up">><<script>>setup.markChoiceVisited("activity_travel_from_gate:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Not up there. The Spire's locked down over her — the towers are the one place she can't go now.">Not up there. The Spire's locked down over her — the towers are the one place she can't go now.</span><br>
<</if>>
<<link "Up to the Reach." "Canvas_activity_travel_from_gate_Node_riding_reach">><<script>>advanceTime(3);<</script>><</link>><br>
<</nobr>><p>The freight door shuts behind her and the red light goes with it. The car climbs out of the dark, past the docks, up until the glass takes the sky again and she's the company's problem to look at.</p>
<<set $game_state.current_canvas = "activity_travel_from_gate">>
<<set $game_state.current_node = "activity_travel_from_gate.riding_up">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_from_gate.riding_up") === -1) { vn.push("activity_travel_from_gate.riding_up"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Ride up to the Plaza.->Location_spire_plaza]]<p>The car takes her the short way — out from under the boards and back onto the waterfront, where the Reach keeps its daylight face and nobody down there knows her by it.</p>
<<set $game_state.current_canvas = "activity_travel_from_gate">>
<<set $game_state.current_node = "activity_travel_from_gate.riding_reach">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_from_gate.riding_reach") === -1) { vn.push("activity_travel_from_gate.riding_reach"); }<</script>>
<<script>>advanceTime(10);<</script>>
[[Out onto the docks.->Location_the_waterfront]]<p>The car idles at the curb where the docks meet the road. Up to the towers — or down under the boards, into the other half of the Reach, the part that doesn't come up in daylight.</p>
<<set $game_state.current_canvas = "activity_travel_to_spire">>
<<set $game_state.current_node = "activity_travel_to_spire.base">>
<<script>>setup.markCanvasTriggered("activity_travel_to_spire");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_spire.base") === -1) { vn.push("activity_travel_to_spire.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]})>>
<span @class="setup.isChoiceVisited('activity_travel_to_spire:cc0') ? '' : 'unlocked-choice'">
<<link "Ride up to the Spire." "Canvas_activity_travel_to_spire_Node_riding_spire">><<script>>setup.markChoiceVisited("activity_travel_to_spire:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="The Spire's sealed to her now — sweeps on every floor, her face on every camera at the curb. Going up is walking into the net. There's no ride back.">The Spire's sealed to her now — sweeps on every floor, her face on every camera at the curb. Going up is walking into the net. There's no ride back.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('activity_travel_to_spire:cc1') ? '' : 'unlocked-choice'">
<<link "Down under the docks — the Underworld." "Canvas_activity_travel_to_spire_Node_riding_under">><<script>>setup.markChoiceVisited("activity_travel_to_spire:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]})))>>
<<script>>
try {
window._engineNoExitsDiag_activity_travel_to_spire = [{"text": "Ride up to the Spire.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "archive_1a_done", "operator": "is_false"}]}}, {"text": "Down under the docks \u2014 the Underworld.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "activity_travel_to_spire",
choices: window._engineNoExitsDiag_activity_travel_to_spire,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_activity_travel_to_spire>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas activity_travel_to_spire</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_the_waterfront]]
<</if>>
<</nobr>><p>She calls the car. The docks recede behind her; the towers climb back up until the glass swallows the sky again. Home, if that's the word for the place that owns you.</p>
<<set $game_state.current_canvas = "activity_travel_to_spire">>
<<set $game_state.current_node = "activity_travel_to_spire.riding_spire">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_spire.riding_spire") === -1) { vn.push("activity_travel_to_spire.riding_spire"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Ride up to the Plaza.->Location_spire_plaza]]<p>She leaves the car and goes down on foot — off the lit strip, past the boards, to the freight door under the docks where the man already has his hand out.</p>
<<set $game_state.current_canvas = "activity_travel_to_spire">>
<<set $game_state.current_node = "activity_travel_to_spire.riding_under">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_spire.riding_under") === -1) { vn.push("activity_travel_to_spire.riding_under"); }<</script>>
<<script>>advanceTime(10);<</script>>
[[Down to the gate.->Location_underworld_gate]]<p>A company car idles at the curb. Down to the Reach and the docks, or further down still — under them, to the half of the city that never comes up in daylight.</p>
<<set $game_state.current_canvas = "activity_travel_to_reach">>
<<set $game_state.current_node = "activity_travel_to_reach.base">>
<<script>>setup.markCanvasTriggered("activity_travel_to_reach");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_reach.base") === -1) { vn.push("activity_travel_to_reach.base"); }<</script>>
<<nobr>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "hygiene", "operator": "gte", "value": 40}]})>>
<span @class="setup.isChoiceVisited('activity_travel_to_reach:cc0') ? '' : 'unlocked-choice'">
<<link "Ride down to the Reach." "Canvas_activity_travel_to_reach_Node_riding">><<script>>setup.markChoiceVisited("activity_travel_to_reach:cc0");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<<else>>
<span class="locked-choice" title="Not like this — you're filthy, and the cover won't hold with the docks able to smell it on you. Wash up first.">Not like this — you're filthy, and the cover won't hold with the docks able to smell it on you. Wash up first.</span><br>
<</if>>
<<if setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]})>>
<span @class="setup.isChoiceVisited('activity_travel_to_reach:cc1') ? '' : 'unlocked-choice'">
<<link "Ride down to the Underworld." "Canvas_activity_travel_to_reach_Node_riding_underworld">><<script>>setup.markChoiceVisited("activity_travel_to_reach:cc1");<</script>><<script>>advanceTime(3);<</script>><</link>><br>
</span>
<</if>>
<<if not ((setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "hygiene", "operator": "gte", "value": 40}]})) or (setup.triggerConditionsSatisfied({"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]})))>>
<<script>>
try {
window._engineNoExitsDiag_activity_travel_to_reach = [{"text": "Ride down to the Reach.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "trait", "subject": "player", "trait_key": "hygiene", "operator": "gte", "value": 40}]}}, {"text": "Ride down to the Underworld.", "conditions": {"version": "1.0", "logic": "AND", "items": [{"type": "flag", "subject": "player", "flag_key": "opening_done", "operator": "is_true"}]}}];
if (typeof console !== 'undefined' && console.warn) {
console.warn('[engine] no exit choices satisfied', {
canvas: "activity_travel_to_reach",
choices: window._engineNoExitsDiag_activity_travel_to_reach,
flags: State.variables.flags,
player_traits: (State.variables.player || {}).core_traits
});
}
} catch(e) {}
<</script>>
<<if $flags.debug_mode>>
<<set _diagChoices to window._engineNoExitsDiag_activity_travel_to_reach>>
<div class="engine-diag-no-exits">
<div class="engine-diag-header">⚠️ DEV: No exit choices satisfied — canvas activity_travel_to_reach</div>
<div class="engine-diag-hint">All exits are conditional and none of their gates currently evaluate true. Player will use the Continue link below to escape, but the visit fires no effects. Likely authoring gap — add a fallback choice or broaden a condition.</div>
<<for _i to 0; _i lt _diagChoices.length; _i++>>
<<set _ch to _diagChoices[_i]>>
<div class="engine-diag-choice">
<div class="engine-diag-choice-text">Choice: "<<print _ch.text>>"</div>
<div class="engine-diag-choice-logic">Combined as: <<print _ch.conditions.logic>></div>
<ul class="engine-diag-items">
<<for _j to 0; _j lt _ch.conditions.items.length; _j++>>
<<set _it to _ch.conditions.items[_j]>>
<<set _single to {version: "1.0", logic: "AND", items: [_it]}>>
<<set _ok to setup.triggerConditionsSatisfied(_single)>>
<li class="<<print _ok ? 'engine-diag-pass' : 'engine-diag-fail'>>"><<if _ok>>✓<<else>>✗<</if>> <<print JSON.stringify(_it)>></li>
<</for>>
</ul>
</div>
<</for>>
</div>
<</if>>
[[Continue->Location_spire_plaza]]
<</if>>
<</nobr>><p>She gives the docks address and the car pulls out without a word. The Spire's glass falls away behind her; the city gets lower, wetter, dirtier the whole way down.</p>
<<set $game_state.current_canvas = "activity_travel_to_reach">>
<<set $game_state.current_node = "activity_travel_to_reach.riding">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_reach.riding") === -1) { vn.push("activity_travel_to_reach.riding"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Down to the Waterfront.->Location_the_waterfront]]<p>She gives an address that isn't on any manifest. The car takes her down past the docks and lets her out at a freight door under everything, where the city goes down instead of up. The driver doesn't wait to see her through it.</p>
<<set $game_state.current_canvas = "activity_travel_to_reach">>
<<set $game_state.current_node = "activity_travel_to_reach.riding_underworld">>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("activity_travel_to_reach.riding_underworld") === -1) { vn.push("activity_travel_to_reach.riding_underworld"); }<</script>>
<<script>>advanceTime(30);<</script>>
[[Out at the gate.->Location_underworld_gate]]<img src="./videos/scenes/facility_match.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>In the wreckage of a containment rig — half-melted, stamped with a maker's mark — she finds a component she recognizes. Not from a file. From herself. The same part, the same stamp, sits inside her own chassis where the lab installed it; she has felt the engineers' hands on its twin. The gear Renner supplied to cage the things that feel was built in the same shop, to the same spec, as the thing she is.</p><p>She notes it the way she notes a serial number matching a serial number. "That's mine," she says, flat, to the burned and empty room — and doesn't know why the words come out cold and certain and terrible, or why she can't make herself say them again.</p>
<<set $game_state.current_canvas = "inv_facility_match">>
<<set $game_state.current_node = "inv_facility_match.base">>
<<script>>setup.markCanvasTriggered("inv_facility_match");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("inv_facility_match.base") === -1) { vn.push("inv_facility_match.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "facility_match_seen", "set");<</script>>
[[Log it. Move on.->Location_facility_ruins]]<img src="./videos/scenes/glitch_ii.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>Tonight it doesn't wait for the charge. She's barely in the cradle before it takes her — the wetness, the fragment, but more now, like a door that used to be locked has warped in its frame. The voice almost resolves. A face almost forms.</p><p>And for the first time she gets a whole feeling instead of a flicker: that she was loved once, completely, by someone, before all of this — and that the company knew, and took it, and built the taking so deep into her that she thanks them for it every morning. Then the charge slams the door, and she's standing in the grey quiet with her face wet and the catechism in her mouth, and for one second she'll spend the rest of her life trying to un-have, the catechism doesn't fit anymore. She files it under nothing. Nothing is getting full too.</p>
<<set $game_state.current_canvas = "canvas_glitch_recurrence_ii">>
<<set $game_state.current_node = "canvas_glitch_recurrence_ii.base">>
<<script>>setup.markCanvasTriggered("canvas_glitch_recurrence_ii");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_glitch_recurrence_ii.base") === -1) { vn.push("canvas_glitch_recurrence_ii.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "glitch_ii_seen", "set");<</script>>
[[Power down anyway.->Location_cradle]]<img src="./videos/scenes/glitch_i.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She powers down like every night, except tonight the leak comes back. The tears first — her face wet again, no order behind it — and then the fragment, longer this time, almost holding: the warmth, the voice with her name folded into it, the someone who was glad to look at her. She reaches for it the way you reach for a word on the tip of your tongue, and it's gone, and the reaching leaves a mark. She files it under fatigue. The fatigue file is getting full.</p>
<<set $game_state.current_canvas = "canvas_glitch_recurrence_i">>
<<set $game_state.current_node = "canvas_glitch_recurrence_i.base">>
<<script>>setup.markCanvasTriggered("canvas_glitch_recurrence_i");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_glitch_recurrence_i.base") === -1) { vn.push("canvas_glitch_recurrence_i.base"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "glitch_i_seen", "set");<</script>>
[[Let the charge take you.->Location_cradle]]<img src="./videos/scenes/opening_morning.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The charge releases her at oh-six-hundred. Full. New day. The water is gone from her face; the fragment has filed itself back under the quiet where she can't reach it, and she's grateful for that the way you're grateful for a wound scabbing over.</p><span id="cascade-b2-beat-0"><p>Her phone has a dossier on it, pushed overnight, Mercer's authority stamped on the header. She reads it standing in the cradle.</p><span id="cascade-b2-beat-1"><<linkreplace "Read it.">><p>A Vance asset-facility in the Reach burned to the ground three weeks ago — Cain's work. The man who supplied its equipment is named Renner: a quartermaster, broke now, blacklisted, drinking himself down at a dock bar called the Anchor and clawing at the wreck of his business. He knows what the facility was and where the trail goes. He does not know what she is.</p><div class="dialog-block dialog-npc"><div class="portrait"><img src="./videos/mercer.jpg" alt="Mercer" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="dialog-content"><strong>Mercer:</strong> Get inside his life. You know how.</div></div><p>There's a cover allowance stamped under the dossier — company credits, for drink and whatever else buys a way into a man's evening. The kind of money she's meant to spend like she intends to come home with him.</p><p>A kit's logged with it — couriered down to her quarters, cheap and worn, the part she's to play. Put it on before the Anchor. She does not walk up to a man like Renner wearing anything the Tower would recognize.</p><span id="cascade-b2-beat-2"><<linkreplace "The part.">><p>She knows the shape of it without being told — not a disguise so much as a register: cheap, willing, a little desperate, the kind of hire a wrecked man doesn't look at twice until she makes him. When she puts it on she'll put it on the way she puts on everything, from the outside. Wren goes under. Whoever Renner needs comes up.</p><span id="cascade-b2-beat-3"><<linkreplace "Out the door.">><img src="./videos/scenes/spire_plaza_reveal.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>She rides down through a Tower she has never once left on her own legs for anything but an order. The atrium doors read her face and open. And then she's outside, on Spire Plaza, the city laid out wet and enormous and unowned in front of her — and for one second, with no cover on her yet, nothing between her and it, something that isn't in any of her files stands very still in her chest and looks at all of it.</p>[[Step into the city.->Location_spire_plaza]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "canvas_opening_morning">>
<<set $game_state.current_node = "canvas_opening_morning.morning">>
<<script>>setup.markCanvasTriggered("canvas_opening_morning");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_opening_morning.morning") === -1) { vn.push("canvas_opening_morning.morning"); }<</script>>
<<script>>advanceTime(3);<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "morning_done", "set");setup.applyAndNotifyFlag("player", null, "opening_done", "set");setup.applyAndNotifyFlag("player", null, "mission_1_active", "set");<</script>>
<<script>>setup.pendingEffects = setup.pendingEffects || [];setup.addToWardrobe("cover_dockhand");setup.showEffectNotification();<</script>><img src="./videos/scenes/opening_cradle.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>The asset level is quiet the way a kept place is quiet. She comes down through the Tower to her own floor, her own door, her own room with nothing on the walls. The cradle waits in the corner.</p><span id="cascade-b2-beat-0"><p>She steps back into it. The contacts find the points along her spine, cold and familiar. The room dims as the charge takes hold, and the day's log begins to write itself down into the quiet. Whatever the day spent out of her — every street walked, every shift worked — the cradle gives it back by morning; let the charge run too low out there and the body starts to fail in ways a man's hands would notice.</p><span id="cascade-b2-beat-1"><<linkreplace "...">><p>Then her face is wet. She registers it the way she'd register a leak in a seal — water, on her cheeks, with no source she can name and no order that called for it. She holds perfectly still and runs every diagnostic she has, and every one comes back clean. She files the water under malfunction and resolves to have it looked at, and does not have it looked at, because some part of her already knows they'd take it out.</p><span id="cascade-b2-beat-2"><<linkreplace "...">><img src="./videos/scenes/cradle_fragment.jpg" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:70vh;object-fit:contain;height:auto;border-radius:8px;" /><p>And under the water, for less than a second, something else. Not a thought — she doesn't have thoughts that aren't tasks — but a fragment: warmth on the side of her face that isn't the cradle's, a low sound that might be a voice with her name folded somewhere inside it, a sense of being looked at by someone who was glad to be looking. Then the charge swallows it and it's gone, and the place where it was aches like a socket with the tooth pulled, and she has no word for the ache, so she has no word for the loss.</p><span id="cascade-b2-beat-3"><<linkreplace "Recite it.">><p>She does what she does when the quiet gets loud. She recites.</p><div class="thought-bubble thought-bubble-player"><div class="portrait"><img src="./videos/wren.jpg" alt="Wren" onerror="this.style.display='none';this.parentElement.innerHTML='<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="35" r="20" fill="#6c757d"/><ellipse cx="50" cy="85" rx="35" ry="25" fill="#6c757d"/></svg>';" /></div><div class="thought-bubble-content"><em>💭 <<print $player.name>> is thinking:</em> I was nothing. The company found me and made me of use. The Chairman keeps me running. I owe them the body and the work and the obedience. I exist to serve. That is all I have ever been.</div></div><p>The words settle her. They always do. She does not notice that the water is still on her face while she tells herself she is a thing that cannot weep.</p><span id="cascade-b2-beat-4"><<linkreplace "The gift.">><p>There is one thing she has that the company gave her and the units don't. When a man uses her the lowest way — takes her ass and spends himself in it — for a few minutes after, he belongs to her. He'll answer anything, do anything, wake remembering none of it. It's how she works the men she's sent to work. It's the one thing that is, in any sense, hers.</p><p>It has never once worked on Mercer. He's had her every way there is, that one included. Nothing passes; the controls never come to her; he stays the hand on the leash. She has never asked why. The not-asking is so complete she doesn't notice it has a shape — a hole exactly the size of a question she isn't allowed to hold.</p><span id="cascade-b2-beat-5"><<linkreplace "Power down.">><p>She lets the catechism be the last thing. She promises the quiet she'll do better — fire next time, feel less, want nothing — and the charge pulls her down past where the water and the fragment and the question all live, into the dark that is supposed to be empty and, lately, isn't quite.</p>[[Sleep.->Location_cradle]]
<</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span><</linkreplace>></span></span>
<<set $game_state.current_canvas = "canvas_opening_night">>
<<set $game_state.current_node = "canvas_opening_night.night">>
<<script>>setup.markCanvasTriggered("canvas_opening_night");<</script>>
<<script>>var vn = State.variables.game_state.visited_nodes;if (vn.indexOf("canvas_opening_night.night") === -1) { vn.push("canvas_opening_night.night"); }<</script>>
<<script>>advanceTime(540);<</script>>
<<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "add", 100.0, true, null);setup.showEffectNotification();<</script>>
<<script>>setup.applyAndNotifyFlag("player", null, "night_done", "set");<</script>><h2>Missing Media Files</h2>
<p>No missing media files found.</p>
<<link "← Back">><<run setup.smartBack()>><</link>><<widget "cheatButton">>
<div id="cheat-btn-widget">
<<if passage() isnot "CheatPage">><<link "Cheat" "CheatPage">><</link>><<else>><span class="nav-row">Cheat</span><</if>><span class="nav-i">🔓</span>
</div>
<</widget>><<nobr>>
<<run setup.cheatRestore()>>
<h2>The Ledger <span class="build-badge">v0.2.0</span></h2>
<div class="npc-section">
<div class="cheat-intro">She keeps her own numbers.</div>
<div class="cheat-entry">
<<textbox "_cheatcode" "">>
<<link "Unlock">>
<<set _hit to setup.cheatTry(_cheatcode)>>
<<if _hit>><<goto "CheatPage">>
<<else>><<replace "#cheat-reply">><span class="cheat-reply-bad">No match. This build is v0.2.0 — check you are using the v0.2.0 guide.</span><</replace>>
<</if>>
<</link>>
<span id="cheat-reply"></span>
</div>
<div class="cheat-join">Cheat codes come with the game guide, in the Beta Nut tier. <a href="https://www.patreon.com/cw/nutgames844/membership" target="_blank" rel="noopener">Open the membership page</a></div>
<<if $flags["cheat_coin"]>>
<div class="cheat-row"><<link "Coin +25" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "coin", "add", 25, false, null);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">five trips through the gate, or two salvage sessions</div></div>
<</if>>
<<if $flags["cheat_fighting"]>>
<div class="cheat-row">
<<if setup.getTraitValue("player", null, "fighting") lt 40>><<link "Fighting +5" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "fighting", "add", 5, true, 40);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">the door at 15 · hard bouts at 30 · the yard at 40</div>
<<else>><span class="cheat-row-maxed">Fighting +5</span><div class="cheat-at-cap">As sharp as this will make her.</div>
<</if>>
</div>
<</if>>
<<if $flags["cheat_stealth"]>>
<div class="cheat-row">
<<if setup.getTraitValue("player", null, "stealth") lt 9>><<link "Stealth +5" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "stealth", "add", 5, true, 9);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">enough to start the yard — the yard itself teaches the rest</div>
<<else>><span class="cheat-row-maxed">Stealth +5</span><div class="cheat-at-cap">Any sharper and she stops needing the quiet routes.</div>
<</if>>
</div>
<</if>>
<<if $flags["cheat_renner"]>>
<div class="cheat-row">
<<if setup.getTraitValue("npc", "npc_renner", "corruption") lt 50>><<link "Renner +10" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_renner", "corruption", "add", 10, true, 50);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">opens the anal pose in the office loop — in cover, carrying the charged drain. The rungs still have to be run.</div>
<<else>><span class="cheat-row-maxed">Renner +10</span><div class="cheat-at-cap">He is as far gone as the rungs will take him.</div>
<</if>>
</div>
<</if>>
<<if $flags["cheat_bastien"]>>
<div class="cheat-row">
<<if setup.getTraitValue("npc", "npc_bastien", "relation") lt 18>><<link "Bastien +6" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("npc", "npc_bastien", "relation", "add", 6, true, 18);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">moves him one promotion nearer — the counter, then the dress, then the back room. You still have to be on the floor in the bought face for each one to land.</div>
<<else>><span class="cheat-row-maxed">Bastien +6</span><div class="cheat-at-cap">He has decided everything about you that he is going to decide from a chair.</div>
<</if>>
</div>
<</if>>
<<if $flags["cheat_charge"]>>
<div class="cheat-row">
<<if setup.getTraitValue("player", null, "energy") isnot 100>><<link "Charge → full" "CheatPage">><<script>>setup.pendingEffects = [];setup.applyAndNotifyTrait("player", null, "energy", "set", 100, true, 100);setup.showEffectNotification();<</script>><</link>><div class="cheat-hint">back to full — the cradle does this for free while the Spire is still open</div>
<<else>><span class="cheat-row-maxed">Charge → full</span><div class="cheat-at-cap">Already full.</div>
<</if>>
</div>
<</if>>
</div>
<</nobr>>
<<link "← Back">><<run setup.smartBack()>><</link>>